2
* Copyright (C) 2019 Apple Inc. All rights reserved.
4
* Redistribution and use in source and binary forms, with or without
5
* modification, are permitted provided that the following conditions
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.
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.
27
#include "WasmEntryPlan.h"
29
#include "WasmBinding.h"
30
#include "WasmFaultSignalHandler.h"
31
#include "WasmMemory.h"
32
#include "WasmSignatureInlines.h"
33
#include <wtf/CrossThreadCopier.h>
34
#include <wtf/DataLog.h>
35
#include <wtf/Locker.h>
36
#include <wtf/MonotonicTime.h>
37
#include <wtf/StdLibExtras.h>
38
#include <wtf/SystemTracing.h>
39
#include <wtf/text/StringConcatenateNumbers.h>
41
#if ENABLE(WEBASSEMBLY)
43
namespace JSC { namespace Wasm {
45
namespace WasmEntryPlanInternal {
46
static const bool verbose = false;
49
EntryPlan::EntryPlan(Context* context, Ref<ModuleInformation> info, AsyncWork work, CompletionTask&& task)
50
: Base(context, WTFMove(info), WTFMove(task))
51
, m_streamingParser(m_moduleInformation.get(), *this)
52
, m_state(State::Validated)
57
EntryPlan::EntryPlan(Context* context, Vector<uint8_t>&& source, AsyncWork work, CompletionTask&& task)
58
: Base(context, WTFMove(task))
59
, m_source(WTFMove(source))
60
, m_streamingParser(m_moduleInformation.get(), *this)
61
, m_state(State::Initial)
66
const char* EntryPlan::stateString(State state)
69
case State::Initial: return "Initial";
70
case State::Validated: return "Validated";
71
case State::Prepared: return "Prepared";
72
case State::Compiled: return "Compiled";
73
case State::Completed: return "Completed";
75
RELEASE_ASSERT_NOT_REACHED();
78
void EntryPlan::moveToState(State state)
80
ASSERT(state >= m_state);
81
dataLogLnIf(WasmEntryPlanInternal::verbose && state != m_state, "moving to state: ", stateString(state), " from state: ", stateString(m_state));
85
bool EntryPlan::parseAndValidateModule(const uint8_t* source, size_t sourceLength)
87
if (m_state != State::Initial)
90
dataLogLnIf(WasmEntryPlanInternal::verbose, "starting validation");
91
MonotonicTime startTime;
92
if (WasmEntryPlanInternal::verbose || Options::reportCompileTimes())
93
startTime = MonotonicTime::now();
95
m_streamingParser.addBytes(source, sourceLength);
97
auto locker = holdLock(m_lock);
102
if (m_streamingParser.finalize() != StreamingParser::State::Finished) {
103
fail(holdLock(m_lock), m_streamingParser.errorMessage());
107
if (WasmEntryPlanInternal::verbose || Options::reportCompileTimes())
108
dataLogLn("Took ", (MonotonicTime::now() - startTime).microseconds(), " us to validate module");
110
moveToState(State::Validated);
114
void EntryPlan::prepare()
116
ASSERT(m_state == State::Validated);
117
dataLogLnIf(WasmEntryPlanInternal::verbose, "Starting preparation");
119
const auto& functions = m_moduleInformation->functions;
120
m_numberOfFunctions = functions.size();
121
if (!tryReserveCapacity(m_wasmToWasmExitStubs, m_moduleInformation->importFunctionSignatureIndices.size(), " WebAssembly to JavaScript stubs")
122
|| !tryReserveCapacity(m_unlinkedWasmToWasmCalls, functions.size(), " unlinked WebAssembly to WebAssembly calls"))
125
m_unlinkedWasmToWasmCalls.resize(functions.size());
127
for (unsigned importIndex = 0; importIndex < m_moduleInformation->imports.size(); ++importIndex) {
128
Import* import = &m_moduleInformation->imports[importIndex];
129
if (import->kind != ExternalKind::Function)
131
unsigned importFunctionIndex = m_wasmToWasmExitStubs.size();
132
dataLogLnIf(WasmEntryPlanInternal::verbose, "Processing import function number ", importFunctionIndex, ": ", makeString(import->module), ": ", makeString(import->field));
133
auto binding = wasmToWasm(importFunctionIndex);
134
if (UNLIKELY(!binding)) {
135
switch (binding.error()) {
136
case BindingFailure::OutOfMemory:
137
return fail(holdLock(m_lock), makeString("Out of executable memory at import ", String::number(importIndex)));
139
RELEASE_ASSERT_NOT_REACHED();
141
m_wasmToWasmExitStubs.uncheckedAppend(binding.value());
144
const uint32_t importFunctionCount = m_moduleInformation->importFunctionCount();
145
for (const auto& exp : m_moduleInformation->exports) {
146
if (exp.kindIndex >= importFunctionCount)
147
m_exportedFunctionIndices.add(exp.kindIndex - importFunctionCount);
150
for (const auto& element : m_moduleInformation->elements) {
151
for (const uint32_t elementIndex : element.functionIndices) {
152
if (elementIndex >= importFunctionCount)
153
m_exportedFunctionIndices.add(elementIndex - importFunctionCount);
157
if (m_moduleInformation->startFunctionIndexSpace && m_moduleInformation->startFunctionIndexSpace >= importFunctionCount)
158
m_exportedFunctionIndices.add(*m_moduleInformation->startFunctionIndexSpace - importFunctionCount);
163
moveToState(State::Prepared);
166
// We don't have a semaphore class... and this does kinda interesting things.
167
class EntryPlan::ThreadCountHolder {
169
ThreadCountHolder(EntryPlan& plan)
172
LockHolder locker(m_plan.m_lock);
173
m_plan.m_numberOfActiveThreads++;
178
LockHolder locker(m_plan.m_lock);
179
m_plan.m_numberOfActiveThreads--;
181
if (!m_plan.m_numberOfActiveThreads && !m_plan.hasWork())
182
m_plan.complete(locker);
189
void EntryPlan::compileFunctions(CompilationEffort effort)
191
ASSERT(m_state >= State::Prepared);
192
dataLogLnIf(WasmEntryPlanInternal::verbose, "Starting compilation");
197
Optional<TraceScope> traceScope;
198
if (Options::useTracePoints())
199
traceScope.emplace(WebAssemblyCompileStart, WebAssemblyCompileEnd);
200
ThreadCountHolder holder(*this);
202
size_t bytesCompiled = 0;
204
if (effort == Partial && bytesCompiled >= Options::webAssemblyPartialCompileLimit())
207
uint32_t functionIndex;
209
auto locker = holdLock(m_lock);
210
if (m_currentIndex >= m_numberOfFunctions) {
212
moveToState(State::Compiled);
215
functionIndex = m_currentIndex;
219
compileFunction(functionIndex);
220
bytesCompiled += m_moduleInformation->functions[functionIndex].data.size();
224
void EntryPlan::complete(const AbstractLocker& locker)
226
ASSERT(m_state != State::Compiled || m_currentIndex >= m_moduleInformation->functions.size());
227
dataLogLnIf(WasmEntryPlanInternal::verbose, "Starting Completion");
229
if (!failed() && m_state == State::Compiled)
230
didCompleteCompilation(locker);
233
moveToState(State::Completed);
234
runCompletionTasks(locker);
239
} } // namespace JSC::Wasm
241
#endif // ENABLE(WEBASSEMBLY)