1 // Copyright 2018 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "src/wasm/wasm-engine.h"
6
7 #include "src/code-tracer.h"
8 #include "src/compilation-statistics.h"
9 #include "src/objects-inl.h"
10 #include "src/objects/js-promise.h"
11 #include "src/wasm/function-compiler.h"
12 #include "src/wasm/module-compiler.h"
13 #include "src/wasm/module-decoder.h"
14 #include "src/wasm/streaming-decoder.h"
15 #include "src/wasm/wasm-objects-inl.h"
16
17 namespace v8 {
18 namespace internal {
19 namespace wasm {
20
WasmEngine()21 WasmEngine::WasmEngine()
22 : code_manager_(&memory_tracker_, kMaxWasmCodeMemory) {}
23
~WasmEngine()24 WasmEngine::~WasmEngine() {
25 // All AsyncCompileJobs have been canceled.
26 DCHECK(jobs_.empty());
27 }
28
SyncValidate(Isolate * isolate,const WasmFeatures & enabled,const ModuleWireBytes & bytes)29 bool WasmEngine::SyncValidate(Isolate* isolate, const WasmFeatures& enabled,
30 const ModuleWireBytes& bytes) {
31 // TODO(titzer): remove dependency on the isolate.
32 if (bytes.start() == nullptr || bytes.length() == 0) return false;
33 ModuleResult result =
34 DecodeWasmModule(enabled, bytes.start(), bytes.end(), true, kWasmOrigin,
35 isolate->counters(), allocator());
36 return result.ok();
37 }
38
SyncCompileTranslatedAsmJs(Isolate * isolate,ErrorThrower * thrower,const ModuleWireBytes & bytes,Handle<Script> asm_js_script,Vector<const byte> asm_js_offset_table_bytes)39 MaybeHandle<WasmModuleObject> WasmEngine::SyncCompileTranslatedAsmJs(
40 Isolate* isolate, ErrorThrower* thrower, const ModuleWireBytes& bytes,
41 Handle<Script> asm_js_script,
42 Vector<const byte> asm_js_offset_table_bytes) {
43 ModuleResult result =
44 DecodeWasmModule(kAsmjsWasmFeatures, bytes.start(), bytes.end(), false,
45 kAsmJsOrigin, isolate->counters(), allocator());
46 CHECK(!result.failed());
47
48 // Transfer ownership of the WasmModule to the {Managed<WasmModule>} generated
49 // in {CompileToModuleObject}.
50 return CompileToModuleObject(isolate, kAsmjsWasmFeatures, thrower,
51 std::move(result.val), bytes, asm_js_script,
52 asm_js_offset_table_bytes);
53 }
54
SyncCompile(Isolate * isolate,const WasmFeatures & enabled,ErrorThrower * thrower,const ModuleWireBytes & bytes)55 MaybeHandle<WasmModuleObject> WasmEngine::SyncCompile(
56 Isolate* isolate, const WasmFeatures& enabled, ErrorThrower* thrower,
57 const ModuleWireBytes& bytes) {
58 ModuleResult result =
59 DecodeWasmModule(enabled, bytes.start(), bytes.end(), false, kWasmOrigin,
60 isolate->counters(), allocator());
61 if (result.failed()) {
62 thrower->CompileFailed("Wasm decoding failed", result);
63 return {};
64 }
65
66 // Transfer ownership of the WasmModule to the {Managed<WasmModule>} generated
67 // in {CompileToModuleObject}.
68 return CompileToModuleObject(isolate, enabled, thrower, std::move(result.val),
69 bytes, Handle<Script>(), Vector<const byte>());
70 }
71
SyncInstantiate(Isolate * isolate,ErrorThrower * thrower,Handle<WasmModuleObject> module_object,MaybeHandle<JSReceiver> imports,MaybeHandle<JSArrayBuffer> memory)72 MaybeHandle<WasmInstanceObject> WasmEngine::SyncInstantiate(
73 Isolate* isolate, ErrorThrower* thrower,
74 Handle<WasmModuleObject> module_object, MaybeHandle<JSReceiver> imports,
75 MaybeHandle<JSArrayBuffer> memory) {
76 return InstantiateToInstanceObject(isolate, thrower, module_object, imports,
77 memory);
78 }
79
AsyncInstantiate(Isolate * isolate,std::unique_ptr<InstantiationResultResolver> resolver,Handle<WasmModuleObject> module_object,MaybeHandle<JSReceiver> imports)80 void WasmEngine::AsyncInstantiate(
81 Isolate* isolate, std::unique_ptr<InstantiationResultResolver> resolver,
82 Handle<WasmModuleObject> module_object, MaybeHandle<JSReceiver> imports) {
83 ErrorThrower thrower(isolate, "WebAssembly Instantiation");
84 // Instantiate a TryCatch so that caught exceptions won't progagate out.
85 // They will still be set as pending exceptions on the isolate.
86 // TODO(clemensh): Avoid TryCatch, use Execution::TryCall internally to invoke
87 // start function and report thrown exception explicitly via out argument.
88 v8::TryCatch catcher(reinterpret_cast<v8::Isolate*>(isolate));
89 catcher.SetVerbose(false);
90 catcher.SetCaptureMessage(false);
91
92 MaybeHandle<WasmInstanceObject> instance_object = SyncInstantiate(
93 isolate, &thrower, module_object, imports, Handle<JSArrayBuffer>::null());
94
95 if (!instance_object.is_null()) {
96 resolver->OnInstantiationSucceeded(instance_object.ToHandleChecked());
97 return;
98 }
99
100 if (isolate->has_pending_exception()) {
101 // The JS code executed during instantiation has thrown an exception.
102 // We have to move the exception to the promise chain.
103 Handle<Object> exception(isolate->pending_exception(), isolate);
104 isolate->clear_pending_exception();
105 DCHECK(*isolate->external_caught_exception_address());
106 *isolate->external_caught_exception_address() = false;
107 resolver->OnInstantiationFailed(exception);
108 thrower.Reset();
109 } else {
110 DCHECK(thrower.error());
111 resolver->OnInstantiationFailed(thrower.Reify());
112 }
113 }
114
AsyncCompile(Isolate * isolate,const WasmFeatures & enabled,std::shared_ptr<CompilationResultResolver> resolver,const ModuleWireBytes & bytes,bool is_shared)115 void WasmEngine::AsyncCompile(
116 Isolate* isolate, const WasmFeatures& enabled,
117 std::shared_ptr<CompilationResultResolver> resolver,
118 const ModuleWireBytes& bytes, bool is_shared) {
119 if (!FLAG_wasm_async_compilation) {
120 // Asynchronous compilation disabled; fall back on synchronous compilation.
121 ErrorThrower thrower(isolate, "WasmCompile");
122 MaybeHandle<WasmModuleObject> module_object;
123 if (is_shared) {
124 // Make a copy of the wire bytes to avoid concurrent modification.
125 std::unique_ptr<uint8_t[]> copy(new uint8_t[bytes.length()]);
126 memcpy(copy.get(), bytes.start(), bytes.length());
127 ModuleWireBytes bytes_copy(copy.get(), copy.get() + bytes.length());
128 module_object = SyncCompile(isolate, enabled, &thrower, bytes_copy);
129 } else {
130 // The wire bytes are not shared, OK to use them directly.
131 module_object = SyncCompile(isolate, enabled, &thrower, bytes);
132 }
133 if (thrower.error()) {
134 resolver->OnCompilationFailed(thrower.Reify());
135 return;
136 }
137 Handle<WasmModuleObject> module = module_object.ToHandleChecked();
138 resolver->OnCompilationSucceeded(module);
139 return;
140 }
141
142 if (FLAG_wasm_test_streaming) {
143 std::shared_ptr<StreamingDecoder> streaming_decoder =
144 StartStreamingCompilation(isolate, enabled,
145 handle(isolate->context(), isolate),
146 std::move(resolver));
147 streaming_decoder->OnBytesReceived(bytes.module_bytes());
148 streaming_decoder->Finish();
149 return;
150 }
151 // Make a copy of the wire bytes in case the user program changes them
152 // during asynchronous compilation.
153 std::unique_ptr<byte[]> copy(new byte[bytes.length()]);
154 memcpy(copy.get(), bytes.start(), bytes.length());
155
156 AsyncCompileJob* job = CreateAsyncCompileJob(
157 isolate, enabled, std::move(copy), bytes.length(),
158 handle(isolate->context(), isolate), std::move(resolver));
159 job->Start();
160 }
161
StartStreamingCompilation(Isolate * isolate,const WasmFeatures & enabled,Handle<Context> context,std::shared_ptr<CompilationResultResolver> resolver)162 std::shared_ptr<StreamingDecoder> WasmEngine::StartStreamingCompilation(
163 Isolate* isolate, const WasmFeatures& enabled, Handle<Context> context,
164 std::shared_ptr<CompilationResultResolver> resolver) {
165 AsyncCompileJob* job =
166 CreateAsyncCompileJob(isolate, enabled, std::unique_ptr<byte[]>(nullptr),
167 0, context, std::move(resolver));
168 return job->CreateStreamingDecoder();
169 }
170
CompileFunction(Isolate * isolate,NativeModule * native_module,uint32_t function_index,ExecutionTier tier)171 bool WasmEngine::CompileFunction(Isolate* isolate, NativeModule* native_module,
172 uint32_t function_index, ExecutionTier tier) {
173 ErrorThrower thrower(isolate, "Manually requested tier up");
174 // Note we assume that "one-off" compilations can discard detected features.
175 WasmFeatures detected = kNoWasmFeatures;
176 WasmCode* ret = WasmCompilationUnit::CompileWasmFunction(
177 isolate, native_module, &detected, &thrower,
178 GetModuleEnv(native_module->compilation_state()),
179 &native_module->module()->functions[function_index], tier);
180 return ret != nullptr;
181 }
182
ExportNativeModule(Handle<WasmModuleObject> module_object)183 std::shared_ptr<NativeModule> WasmEngine::ExportNativeModule(
184 Handle<WasmModuleObject> module_object) {
185 return module_object->managed_native_module()->get();
186 }
187
ImportNativeModule(Isolate * isolate,std::shared_ptr<NativeModule> shared_module)188 Handle<WasmModuleObject> WasmEngine::ImportNativeModule(
189 Isolate* isolate, std::shared_ptr<NativeModule> shared_module) {
190 CHECK_EQ(code_manager(), shared_module->code_manager());
191 Vector<const byte> wire_bytes = shared_module->wire_bytes();
192 Handle<Script> script = CreateWasmScript(isolate, wire_bytes);
193 Handle<WasmModuleObject> module_object =
194 WasmModuleObject::New(isolate, shared_module, script);
195
196 // TODO(6792): Wrappers below might be cloned using {Factory::CopyCode}.
197 // This requires unlocking the code space here. This should eventually be
198 // moved into the allocator.
199 CodeSpaceMemoryModificationScope modification_scope(isolate->heap());
200 CompileJsToWasmWrappers(isolate, module_object);
201 return module_object;
202 }
203
GetOrCreateTurboStatistics()204 CompilationStatistics* WasmEngine::GetOrCreateTurboStatistics() {
205 base::LockGuard<base::Mutex> guard(&mutex_);
206 if (compilation_stats_ == nullptr) {
207 compilation_stats_.reset(new CompilationStatistics());
208 }
209 return compilation_stats_.get();
210 }
211
DumpAndResetTurboStatistics()212 void WasmEngine::DumpAndResetTurboStatistics() {
213 base::LockGuard<base::Mutex> guard(&mutex_);
214 if (compilation_stats_ != nullptr) {
215 StdoutStream os;
216 os << AsPrintableStatistics{*compilation_stats_.get(), false} << std::endl;
217 }
218 compilation_stats_.reset();
219 }
220
GetCodeTracer()221 CodeTracer* WasmEngine::GetCodeTracer() {
222 base::LockGuard<base::Mutex> guard(&mutex_);
223 if (code_tracer_ == nullptr) code_tracer_.reset(new CodeTracer(-1));
224 return code_tracer_.get();
225 }
226
CreateAsyncCompileJob(Isolate * isolate,const WasmFeatures & enabled,std::unique_ptr<byte[]> bytes_copy,size_t length,Handle<Context> context,std::shared_ptr<CompilationResultResolver> resolver)227 AsyncCompileJob* WasmEngine::CreateAsyncCompileJob(
228 Isolate* isolate, const WasmFeatures& enabled,
229 std::unique_ptr<byte[]> bytes_copy, size_t length, Handle<Context> context,
230 std::shared_ptr<CompilationResultResolver> resolver) {
231 AsyncCompileJob* job =
232 new AsyncCompileJob(isolate, enabled, std::move(bytes_copy), length,
233 context, std::move(resolver));
234 // Pass ownership to the unique_ptr in {jobs_}.
235 base::LockGuard<base::Mutex> guard(&mutex_);
236 jobs_[job] = std::unique_ptr<AsyncCompileJob>(job);
237 return job;
238 }
239
RemoveCompileJob(AsyncCompileJob * job)240 std::unique_ptr<AsyncCompileJob> WasmEngine::RemoveCompileJob(
241 AsyncCompileJob* job) {
242 base::LockGuard<base::Mutex> guard(&mutex_);
243 auto item = jobs_.find(job);
244 DCHECK(item != jobs_.end());
245 std::unique_ptr<AsyncCompileJob> result = std::move(item->second);
246 jobs_.erase(item);
247 return result;
248 }
249
HasRunningCompileJob(Isolate * isolate)250 bool WasmEngine::HasRunningCompileJob(Isolate* isolate) {
251 base::LockGuard<base::Mutex> guard(&mutex_);
252 for (auto& entry : jobs_) {
253 if (entry.first->isolate() == isolate) return true;
254 }
255 return false;
256 }
257
DeleteCompileJobsOnIsolate(Isolate * isolate)258 void WasmEngine::DeleteCompileJobsOnIsolate(Isolate* isolate) {
259 base::LockGuard<base::Mutex> guard(&mutex_);
260 for (auto it = jobs_.begin(); it != jobs_.end();) {
261 if (it->first->isolate() == isolate) {
262 it = jobs_.erase(it);
263 } else {
264 ++it;
265 }
266 }
267 }
268
269 namespace {
270
271 struct WasmEnginePointerConstructTrait final {
Constructv8::internal::wasm::__anon59ee92040111::WasmEnginePointerConstructTrait272 static void Construct(void* raw_ptr) {
273 auto engine_ptr = reinterpret_cast<std::shared_ptr<WasmEngine>*>(raw_ptr);
274 *engine_ptr = std::shared_ptr<WasmEngine>();
275 }
276 };
277
278 // Holds the global shared pointer to the single {WasmEngine} that is intended
279 // to be shared among Isolates within the same process. The {LazyStaticInstance}
280 // here is required because {std::shared_ptr} has a non-trivial initializer.
281 base::LazyStaticInstance<std::shared_ptr<WasmEngine>,
282 WasmEnginePointerConstructTrait>::type
283 global_wasm_engine;
284
285 } // namespace
286
InitializeOncePerProcess()287 void WasmEngine::InitializeOncePerProcess() {
288 if (!FLAG_wasm_shared_engine) return;
289 global_wasm_engine.Pointer()->reset(new WasmEngine());
290 }
291
GlobalTearDown()292 void WasmEngine::GlobalTearDown() {
293 if (!FLAG_wasm_shared_engine) return;
294 global_wasm_engine.Pointer()->reset();
295 }
296
GetWasmEngine()297 std::shared_ptr<WasmEngine> WasmEngine::GetWasmEngine() {
298 if (FLAG_wasm_shared_engine) return global_wasm_engine.Get();
299 return std::shared_ptr<WasmEngine>(new WasmEngine());
300 }
301
302 } // namespace wasm
303 } // namespace internal
304 } // namespace v8
305