1/*
2 * Copyright (C) 2019 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 "WasmLLIntPlan.h"
28
29#if ENABLE(WEBASSEMBLY)
30
31#include "B3Compilation.h"
32#include "BytecodeDumper.h"
33#include "CalleeBits.h"
34#include "LLIntThunks.h"
35#include "LinkBuffer.h"
36#include "WasmCallee.h"
37#include "WasmLLIntGenerator.h"
38#include "WasmSignatureInlines.h"
39#include "WasmValidate.h"
40
41namespace JSC { namespace Wasm {
42
43namespace WasmLLIntPlanInternal {
44static const bool verbose = false;
45}
46
47bool LLIntPlan::prepareImpl()
48{
49 const auto& functions = m_moduleInformation->functions;
50 if (!tryReserveCapacity(m_wasmInternalFunctions, functions.size(), " WebAssembly functions"))
51 return false;
52
53 m_wasmInternalFunctions.resize(functions.size());
54
55 return true;
56}
57
58void LLIntPlan::compileFunction(uint32_t functionIndex)
59{
60 const auto& function = m_moduleInformation->functions[functionIndex];
61 SignatureIndex signatureIndex = m_moduleInformation->internalFunctionSignatureIndices[functionIndex];
62 const Signature& signature = SignatureInformation::get(signatureIndex);
63 unsigned functionIndexSpace = m_wasmToWasmExitStubs.size() + functionIndex;
64 ASSERT_UNUSED(functionIndexSpace, m_moduleInformation->signatureIndexFromFunctionIndexSpace(functionIndexSpace) == signatureIndex);
65 ASSERT(validateFunction(function, signature, m_moduleInformation.get()));
66
67 m_unlinkedWasmToWasmCalls[functionIndex] = Vector<UnlinkedWasmToWasmCall>();
68 Expected<std::unique_ptr<FunctionCodeBlock>, String> parseAndCompileResult = parseAndCompileBytecode(function.data.data(), function.data.size(), signature, m_moduleInformation.get(), functionIndex, m_throwWasmException);
69
70 if (UNLIKELY(!parseAndCompileResult)) {
71 auto locker = holdLock(m_lock);
72 if (!m_errorMessage) {
73 // Multiple compiles could fail simultaneously. We arbitrarily choose the first.
74 fail(locker, makeString(parseAndCompileResult.error(), ", in function at index ", String::number(functionIndex))); // FIXME make this an Expected.
75 }
76 m_currentIndex = m_moduleInformation->functions.size();
77 return;
78 }
79
80 m_wasmInternalFunctions[functionIndex] = WTFMove(*parseAndCompileResult);
81
82 if (m_exportedFunctionIndices.contains(functionIndex) || m_moduleInformation->referencedFunctions().contains(functionIndex)) {
83 auto locker = holdLock(m_lock);
84 EmbederToWasmFunction entry;
85 entry.jit = makeUnique<CCallHelpers>();
86 entry.function = m_createEmbedderWrapper(*entry.jit, signature, &m_unlinkedWasmToWasmCalls[functionIndex], m_moduleInformation.get(), m_mode, functionIndex);
87 auto result = m_embedderToWasmInternalFunctions.add(functionIndex, WTFMove(entry));
88 ASSERT_UNUSED(result, result.isNewEntry);
89 }
90}
91
92void LLIntPlan::didCompleteCompilation(const AbstractLocker& locker)
93{
94 for (uint32_t functionIndex = 0; functionIndex < m_moduleInformation->functions.size(); functionIndex++) {
95 SignatureIndex signatureIndex = m_moduleInformation->internalFunctionSignatureIndices[functionIndex];
96 const Signature& signature = SignatureInformation::get(signatureIndex);
97
98 if (auto it = m_embedderToWasmInternalFunctions.find(functionIndex); it != m_embedderToWasmInternalFunctions.end()) {
99 LinkBuffer linkBuffer(*it->value.jit, nullptr, JITCompilationCanFail);
100 if (UNLIKELY(linkBuffer.didFailToAllocate())) {
101 Base::fail(locker, makeString("Out of executable memory in function entrypoint at index ", String::number(functionIndex)));
102 return;
103 }
104
105 it->value.function->entrypoint.compilation = makeUnique<B3::Compilation>(
106 FINALIZE_CODE(linkBuffer, B3CompilationPtrTag, "Embedder->WebAssembly entrypoint[%i] %s", functionIndex, signature.toString().ascii().data()),
107 nullptr);
108 }
109 }
110
111 unsigned functionCount = m_wasmInternalFunctions.size();
112 if (functionCount) {
113 // LLInt entrypoint thunks generation
114 CCallHelpers jit;
115 m_callees.resize(functionCount);
116 Vector<CCallHelpers::Label> entrypoints(functionCount);
117 Vector<CCallHelpers::Jump> jumps(functionCount);
118 for (unsigned i = 0; i < functionCount; ++i) {
119 size_t functionIndexSpace = i + m_moduleInformation->importFunctionCount();
120
121 if (UNLIKELY(Options::dumpGeneratedWasmBytecodes()))
122 BytecodeDumper::dumpBlock(m_wasmInternalFunctions[i].get(), m_moduleInformation, WTF::dataFile());
123
124 m_callees[i] = Wasm::LLIntCallee::create(WTFMove(m_wasmInternalFunctions[i]), functionIndexSpace, m_moduleInformation->nameSection->get(functionIndexSpace));
125 entrypoints[i] = jit.label();
126#if CPU(X86_64)
127 CCallHelpers::Address calleeSlot(CCallHelpers::stackPointerRegister, CallFrameSlot::callee * static_cast<int>(sizeof(Register)) - sizeof(CPURegister));
128#elif CPU(ARM64)
129 CCallHelpers::Address calleeSlot(CCallHelpers::stackPointerRegister, CallFrameSlot::callee * static_cast<int>(sizeof(Register)) - sizeof(CallerFrameAndPC));
130#else
131#error Unsupported architecture.
132#endif
133 jit.storePtr(CCallHelpers::TrustedImmPtr(CalleeBits::boxWasm(m_callees[i].ptr())), calleeSlot);
134 jumps[i] = jit.jump();
135 }
136
137 LinkBuffer linkBuffer(jit, GLOBAL_THUNK_ID, JITCompilationCanFail);
138 if (UNLIKELY(linkBuffer.didFailToAllocate())) {
139 Base::fail(locker, "Out of executable memory in Wasm LLInt entry thunks");
140 return;
141 }
142
143 for (unsigned i = 0; i < functionCount; ++i) {
144 m_callees[i]->setEntrypoint(linkBuffer.locationOf<WasmEntryPtrTag>(entrypoints[i]));
145 linkBuffer.link<JITThunkPtrTag>(jumps[i], CodeLocationLabel<JITThunkPtrTag>(LLInt::wasmFunctionEntryThunk().code()));
146 }
147
148 m_entryThunks = FINALIZE_CODE(linkBuffer, B3CompilationPtrTag, "Wasm LLInt entry thunks");
149 }
150
151 for (auto& unlinked : m_unlinkedWasmToWasmCalls) {
152 for (auto& call : unlinked) {
153 MacroAssemblerCodePtr<WasmEntryPtrTag> executableAddress;
154 if (m_moduleInformation->isImportedFunctionFromFunctionIndexSpace(call.functionIndexSpace)) {
155 // 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
156 executableAddress = m_wasmToWasmExitStubs.at(call.functionIndexSpace).code();
157 } else
158 executableAddress = m_callees[call.functionIndexSpace - m_moduleInformation->importFunctionCount()]->entrypoint();
159 MacroAssembler::repatchNearCall(call.callLocation, CodeLocationLabel<WasmEntryPtrTag>(executableAddress));
160 }
161 }
162}
163
164void LLIntPlan::initializeCallees(const CalleeInitializer& callback)
165{
166 ASSERT(!failed());
167 for (unsigned internalFunctionIndex = 0; internalFunctionIndex < m_wasmInternalFunctions.size(); ++internalFunctionIndex) {
168 RefPtr<Wasm::Callee> embedderEntrypointCallee;
169 if (auto it = m_embedderToWasmInternalFunctions.find(internalFunctionIndex); it != m_embedderToWasmInternalFunctions.end()) {
170 embedderEntrypointCallee = Wasm::EmbedderEntrypointCallee::create(WTFMove(it->value.function->entrypoint));
171 if (it->value.function->calleeMoveLocation)
172 MacroAssembler::repatchPointer(it->value.function->calleeMoveLocation, CalleeBits::boxWasm(embedderEntrypointCallee.get()));
173 }
174
175 callback(internalFunctionIndex, WTFMove(embedderEntrypointCallee), WTFMove(m_callees[internalFunctionIndex]));
176 }
177}
178
179} } // namespace JSC::Wasm
180
181#endif // ENABLE(WEBASSEMBLY)
182