1/*
2 * Copyright (C) 2017-2018 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 "WasmCodeBlock.h"
28
29#if ENABLE(WEBASSEMBLY)
30
31#include "WasmBBQPlan.h"
32#include "WasmCallee.h"
33#include "WasmFormat.h"
34#include "WasmLLIntPlan.h"
35#include "WasmWorklist.h"
36
37namespace JSC { namespace Wasm {
38
39Ref<CodeBlock> CodeBlock::create(Context* context, MemoryMode mode, ModuleInformation& moduleInformation, CreateEmbedderWrapper&& createEmbedderWrapper, ThrowWasmException throwWasmException)
40{
41 auto* result = new (NotNull, fastMalloc(sizeof(CodeBlock))) CodeBlock(context, mode, moduleInformation, WTFMove(createEmbedderWrapper), throwWasmException);
42 return adoptRef(*result);
43}
44
45CodeBlock::CodeBlock(Context* context, MemoryMode mode, ModuleInformation& moduleInformation, CreateEmbedderWrapper&& createEmbedderWrapper, ThrowWasmException throwWasmException)
46 : m_calleeCount(moduleInformation.internalFunctionCount())
47 , m_mode(mode)
48{
49 RefPtr<CodeBlock> protectedThis = this;
50
51 auto task = createSharedTask<Plan::CallbackType>([this, protectedThis = WTFMove(protectedThis)] (Plan&) {
52 auto locker = holdLock(m_lock);
53 if (m_plan->failed()) {
54 m_errorMessage = m_plan->errorMessage();
55 setCompilationFinished();
56 return;
57 }
58
59 // FIXME: we should eventually collect the BBQ code.
60 m_llintCallees.resize(m_calleeCount);
61 m_bbqCallees.resize(m_calleeCount);
62 m_omgCallees.resize(m_calleeCount);
63 m_wasmIndirectCallEntryPoints.resize(m_calleeCount);
64
65 m_plan->initializeCallees([&] (unsigned calleeIndex, RefPtr<Callee>&& embedderEntrypointCallee, RefPtr<Callee>&& wasmEntrypoint) {
66 if (embedderEntrypointCallee) {
67 auto result = m_embedderCallees.set(calleeIndex, WTFMove(embedderEntrypointCallee));
68 ASSERT_UNUSED(result, result.isNewEntry);
69 }
70 m_wasmIndirectCallEntryPoints[calleeIndex] = wasmEntrypoint->entrypoint();
71
72 if (Options::useWasmLLInt())
73 m_llintCallees[calleeIndex] = adoptRef(static_cast<LLIntCallee*>(wasmEntrypoint.leakRef()));
74 else
75 m_bbqCallees[calleeIndex] = adoptRef(static_cast<BBQCallee*>(wasmEntrypoint.leakRef()));
76 });
77
78 m_wasmToWasmExitStubs = m_plan->takeWasmToWasmExitStubs();
79 m_wasmToWasmCallsites = m_plan->takeWasmToWasmCallsites();
80
81 if (Options::useWasmLLInt())
82 m_llintEntryThunks = bitwise_cast<LLIntPlan*>(m_plan.get())->takeEntryThunks();
83
84 setCompilationFinished();
85 });
86
87 if (Options::useWasmLLInt())
88 m_plan = adoptRef(*new LLIntPlan(context, makeRef(moduleInformation), EntryPlan::FullCompile, WTFMove(task), WTFMove(createEmbedderWrapper), throwWasmException));
89 else
90 m_plan = adoptRef(*new BBQPlan(context, makeRef(moduleInformation), EntryPlan::FullCompile, WTFMove(task), WTFMove(createEmbedderWrapper), throwWasmException));
91 m_plan->setMode(mode);
92
93 auto& worklist = Wasm::ensureWorklist();
94 // Note, immediately after we enqueue the plan, there is a chance the above callback will be called.
95 worklist.enqueue(makeRef(*m_plan.get()));
96}
97
98CodeBlock::~CodeBlock() { }
99
100void CodeBlock::waitUntilFinished()
101{
102 RefPtr<Plan> plan;
103 {
104 auto locker = holdLock(m_lock);
105 plan = m_plan;
106 }
107
108 if (plan) {
109 auto& worklist = Wasm::ensureWorklist();
110 worklist.completePlanSynchronously(*plan.get());
111 }
112 // else, if we don't have a plan, we're already compiled.
113}
114
115void CodeBlock::compileAsync(Context* context, AsyncCompilationCallback&& task)
116{
117 RefPtr<Plan> plan;
118 {
119 auto locker = holdLock(m_lock);
120 plan = m_plan;
121 }
122
123 if (plan) {
124 // We don't need to keep a RefPtr on the Plan because the worklist will keep
125 // a RefPtr on the Plan until the plan finishes notifying all of its callbacks.
126 RefPtr<CodeBlock> protectedThis = this;
127 plan->addCompletionTask(context, createSharedTask<Plan::CallbackType>([this, task = WTFMove(task), protectedThis = WTFMove(protectedThis)] (Plan&) {
128 task->run(makeRef(*this));
129 }));
130 } else
131 task->run(makeRef(*this));
132}
133
134bool CodeBlock::isSafeToRun(MemoryMode memoryMode)
135{
136 if (!runnable())
137 return false;
138
139 switch (m_mode) {
140 case Wasm::MemoryMode::BoundsChecking:
141 return true;
142 case Wasm::MemoryMode::Signaling:
143 // Code being in Signaling mode means that it performs no bounds checks.
144 // Its memory, even if empty, absolutely must also be in Signaling mode
145 // because the page protection detects out-of-bounds accesses.
146 return memoryMode == Wasm::MemoryMode::Signaling;
147 }
148 RELEASE_ASSERT_NOT_REACHED();
149 return false;
150}
151
152
153void CodeBlock::setCompilationFinished()
154{
155 m_plan = nullptr;
156 m_compilationFinished.store(true);
157}
158
159} } // namespace JSC::Wasm
160
161#endif // ENABLE(WEBASSEMBLY)
162