Line data Source code
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/heap-number.h"
11 : #include "src/objects/js-promise.h"
12 : #include "src/wasm/function-compiler.h"
13 : #include "src/wasm/module-compiler.h"
14 : #include "src/wasm/module-decoder.h"
15 : #include "src/wasm/module-instantiate.h"
16 : #include "src/wasm/streaming-decoder.h"
17 : #include "src/wasm/wasm-objects-inl.h"
18 :
19 : namespace v8 {
20 : namespace internal {
21 : namespace wasm {
22 :
23 61326 : WasmEngine::WasmEngine()
24 183978 : : code_manager_(&memory_tracker_, FLAG_wasm_max_code_space * MB) {}
25 :
26 63766 : WasmEngine::~WasmEngine() {
27 : // All AsyncCompileJobs have been canceled.
28 : DCHECK(jobs_.empty());
29 : // All Isolates have been deregistered.
30 : DCHECK(isolates_.empty());
31 31883 : }
32 :
33 142256 : bool WasmEngine::SyncValidate(Isolate* isolate, const WasmFeatures& enabled,
34 : const ModuleWireBytes& bytes) {
35 : // TODO(titzer): remove dependency on the isolate.
36 284512 : if (bytes.start() == nullptr || bytes.length() == 0) return false;
37 : ModuleResult result =
38 : DecodeWasmModule(enabled, bytes.start(), bytes.end(), true, kWasmOrigin,
39 426768 : isolate->counters(), allocator());
40 142256 : return result.ok();
41 : }
42 :
43 2985 : MaybeHandle<AsmWasmData> WasmEngine::SyncCompileTranslatedAsmJs(
44 : Isolate* isolate, ErrorThrower* thrower, const ModuleWireBytes& bytes,
45 : Vector<const byte> asm_js_offset_table_bytes,
46 : Handle<HeapNumber> uses_bitset) {
47 : ModuleResult result =
48 : DecodeWasmModule(kAsmjsWasmFeatures, bytes.start(), bytes.end(), false,
49 8955 : kAsmJsOrigin, isolate->counters(), allocator());
50 2985 : CHECK(!result.failed());
51 :
52 : // Transfer ownership of the WasmModule to the {Managed<WasmModule>} generated
53 : // in {CompileToNativeModule}.
54 : Handle<FixedArray> export_wrappers;
55 : std::unique_ptr<NativeModule> native_module =
56 : CompileToNativeModule(isolate, kAsmjsWasmFeatures, thrower,
57 5970 : std::move(result).value(), bytes, &export_wrappers);
58 2985 : if (!native_module) return {};
59 :
60 : // Create heap objects for asm.js offset table to be stored in the module
61 : // object.
62 : Handle<ByteArray> asm_js_offset_table =
63 2985 : isolate->factory()->NewByteArray(asm_js_offset_table_bytes.length());
64 : asm_js_offset_table->copy_in(0, asm_js_offset_table_bytes.start(),
65 2985 : asm_js_offset_table_bytes.length());
66 :
67 : return AsmWasmData::New(isolate, std::move(native_module), export_wrappers,
68 8955 : asm_js_offset_table, uses_bitset);
69 : }
70 :
71 5636 : Handle<WasmModuleObject> WasmEngine::FinalizeTranslatedAsmJs(
72 : Isolate* isolate, Handle<AsmWasmData> asm_wasm_data,
73 : Handle<Script> script) {
74 : std::shared_ptr<NativeModule> native_module =
75 11272 : asm_wasm_data->managed_native_module()->get();
76 : Handle<FixedArray> export_wrappers =
77 11272 : handle(asm_wasm_data->export_wrappers(), isolate);
78 : size_t code_size_estimate =
79 : wasm::WasmCodeManager::EstimateNativeModuleCodeSize(
80 11272 : native_module->module());
81 :
82 : Handle<WasmModuleObject> module_object =
83 : WasmModuleObject::New(isolate, std::move(native_module), script,
84 11272 : export_wrappers, code_size_estimate);
85 11272 : module_object->set_asm_js_offset_table(asm_wasm_data->asm_js_offset_table());
86 11272 : return module_object;
87 : }
88 :
89 167330 : MaybeHandle<WasmModuleObject> WasmEngine::SyncCompile(
90 150488 : Isolate* isolate, const WasmFeatures& enabled, ErrorThrower* thrower,
91 : const ModuleWireBytes& bytes) {
92 : ModuleResult result =
93 : DecodeWasmModule(enabled, bytes.start(), bytes.end(), false, kWasmOrigin,
94 502007 : isolate->counters(), allocator());
95 167347 : if (result.failed()) {
96 : thrower->CompileFailed("Wasm decoding failed", result.error());
97 10167 : return {};
98 : }
99 :
100 : // Transfer ownership of the WasmModule to the {Managed<WasmModule>} generated
101 : // in {CompileToModuleObject}.
102 : Handle<FixedArray> export_wrappers;
103 : std::unique_ptr<NativeModule> native_module =
104 : CompileToNativeModule(isolate, enabled, thrower,
105 314362 : std::move(result).value(), bytes, &export_wrappers);
106 157182 : if (!native_module) return {};
107 :
108 : Handle<Script> script =
109 150494 : CreateWasmScript(isolate, bytes, native_module->module()->source_map_url);
110 : size_t code_size_estimate =
111 : wasm::WasmCodeManager::EstimateNativeModuleCodeSize(
112 150491 : native_module->module());
113 :
114 : // Create the module object.
115 : // TODO(clemensh): For the same module (same bytes / same hash), we should
116 : // only have one WasmModuleObject. Otherwise, we might only set
117 : // breakpoints on a (potentially empty) subset of the instances.
118 :
119 : // Create the compiled module object and populate with compiled functions
120 : // and information needed at instantiation time. This object needs to be
121 : // serializable. Instantiation may occur off a deserialized version of this
122 : // object.
123 : Handle<WasmModuleObject> module_object =
124 : WasmModuleObject::New(isolate, std::move(native_module), script,
125 300980 : export_wrappers, code_size_estimate);
126 :
127 : // Finish the Wasm script now and make it public to the debugger.
128 150488 : isolate->debug()->OnAfterCompile(script);
129 317843 : return module_object;
130 : }
131 :
132 150012 : MaybeHandle<WasmInstanceObject> WasmEngine::SyncInstantiate(
133 : Isolate* isolate, ErrorThrower* thrower,
134 : Handle<WasmModuleObject> module_object, MaybeHandle<JSReceiver> imports,
135 : MaybeHandle<JSArrayBuffer> memory) {
136 : return InstantiateToInstanceObject(isolate, thrower, module_object, imports,
137 152596 : memory);
138 : }
139 :
140 2584 : void WasmEngine::AsyncInstantiate(
141 : Isolate* isolate, std::unique_ptr<InstantiationResultResolver> resolver,
142 : Handle<WasmModuleObject> module_object, MaybeHandle<JSReceiver> imports) {
143 : ErrorThrower thrower(isolate, "WebAssembly Instantiation");
144 : // Instantiate a TryCatch so that caught exceptions won't progagate out.
145 : // They will still be set as pending exceptions on the isolate.
146 : // TODO(clemensh): Avoid TryCatch, use Execution::TryCall internally to invoke
147 : // start function and report thrown exception explicitly via out argument.
148 3745 : v8::TryCatch catcher(reinterpret_cast<v8::Isolate*>(isolate));
149 2584 : catcher.SetVerbose(false);
150 2584 : catcher.SetCaptureMessage(false);
151 :
152 : MaybeHandle<WasmInstanceObject> instance_object = SyncInstantiate(
153 : isolate, &thrower, module_object, imports, Handle<JSArrayBuffer>::null());
154 :
155 2584 : if (!instance_object.is_null()) {
156 2846 : resolver->OnInstantiationSucceeded(instance_object.ToHandleChecked());
157 4007 : return;
158 : }
159 :
160 1161 : if (isolate->has_pending_exception()) {
161 : // The JS code executed during instantiation has thrown an exception.
162 : // We have to move the exception to the promise chain.
163 : Handle<Object> exception(isolate->pending_exception(), isolate);
164 45 : isolate->clear_pending_exception();
165 : DCHECK(*isolate->external_caught_exception_address());
166 45 : *isolate->external_caught_exception_address() = false;
167 45 : resolver->OnInstantiationFailed(exception);
168 45 : thrower.Reset();
169 : } else {
170 : DCHECK(thrower.error());
171 1116 : resolver->OnInstantiationFailed(thrower.Reify());
172 1161 : }
173 : }
174 :
175 2470 : void WasmEngine::AsyncCompile(
176 : Isolate* isolate, const WasmFeatures& enabled,
177 : std::shared_ptr<CompilationResultResolver> resolver,
178 : const ModuleWireBytes& bytes, bool is_shared) {
179 2470 : if (!FLAG_wasm_async_compilation) {
180 : // Asynchronous compilation disabled; fall back on synchronous compilation.
181 : ErrorThrower thrower(isolate, "WasmCompile");
182 : MaybeHandle<WasmModuleObject> module_object;
183 0 : if (is_shared) {
184 : // Make a copy of the wire bytes to avoid concurrent modification.
185 0 : std::unique_ptr<uint8_t[]> copy(new uint8_t[bytes.length()]);
186 : memcpy(copy.get(), bytes.start(), bytes.length());
187 0 : ModuleWireBytes bytes_copy(copy.get(), copy.get() + bytes.length());
188 0 : module_object = SyncCompile(isolate, enabled, &thrower, bytes_copy);
189 : } else {
190 : // The wire bytes are not shared, OK to use them directly.
191 0 : module_object = SyncCompile(isolate, enabled, &thrower, bytes);
192 : }
193 0 : if (thrower.error()) {
194 0 : resolver->OnCompilationFailed(thrower.Reify());
195 0 : return;
196 : }
197 0 : Handle<WasmModuleObject> module = module_object.ToHandleChecked();
198 0 : resolver->OnCompilationSucceeded(module);
199 0 : return;
200 : }
201 :
202 2470 : if (FLAG_wasm_test_streaming) {
203 : std::shared_ptr<StreamingDecoder> streaming_decoder =
204 : StartStreamingCompilation(isolate, enabled,
205 : handle(isolate->context(), isolate),
206 418 : std::move(resolver));
207 209 : streaming_decoder->OnBytesReceived(bytes.module_bytes());
208 209 : streaming_decoder->Finish();
209 : return;
210 : }
211 : // Make a copy of the wire bytes in case the user program changes them
212 : // during asynchronous compilation.
213 2261 : std::unique_ptr<byte[]> copy(new byte[bytes.length()]);
214 : memcpy(copy.get(), bytes.start(), bytes.length());
215 :
216 : AsyncCompileJob* job = CreateAsyncCompileJob(
217 : isolate, enabled, std::move(copy), bytes.length(),
218 6783 : handle(isolate->context(), isolate), std::move(resolver));
219 2261 : job->Start();
220 : }
221 :
222 424 : std::shared_ptr<StreamingDecoder> WasmEngine::StartStreamingCompilation(
223 : Isolate* isolate, const WasmFeatures& enabled, Handle<Context> context,
224 : std::shared_ptr<CompilationResultResolver> resolver) {
225 : AsyncCompileJob* job =
226 : CreateAsyncCompileJob(isolate, enabled, std::unique_ptr<byte[]>(nullptr),
227 1272 : 0, context, std::move(resolver));
228 424 : return job->CreateStreamingDecoder();
229 : }
230 :
231 18 : void WasmEngine::CompileFunction(Isolate* isolate, NativeModule* native_module,
232 : uint32_t function_index, ExecutionTier tier) {
233 : // Note we assume that "one-off" compilations can discard detected features.
234 18 : WasmFeatures detected = kNoWasmFeatures;
235 : WasmCompilationUnit::CompileWasmFunction(
236 : isolate, native_module, &detected,
237 54 : &native_module->module()->functions[function_index], tier);
238 18 : }
239 :
240 0 : std::shared_ptr<NativeModule> WasmEngine::ExportNativeModule(
241 : Handle<WasmModuleObject> module_object) {
242 0 : return module_object->shared_native_module();
243 : }
244 :
245 153 : Handle<WasmModuleObject> WasmEngine::ImportNativeModule(
246 : Isolate* isolate, std::shared_ptr<NativeModule> shared_module) {
247 306 : ModuleWireBytes wire_bytes(shared_module->wire_bytes());
248 : const WasmModule* module = shared_module->module();
249 : Handle<Script> script =
250 153 : CreateWasmScript(isolate, wire_bytes, module->source_map_url);
251 : size_t code_size = shared_module->committed_code_space();
252 : Handle<WasmModuleObject> module_object = WasmModuleObject::New(
253 306 : isolate, std::move(shared_module), script, code_size);
254 : CompileJsToWasmWrappers(isolate, module_object->native_module()->module(),
255 459 : handle(module_object->export_wrappers(), isolate));
256 153 : return module_object;
257 : }
258 :
259 0 : CompilationStatistics* WasmEngine::GetOrCreateTurboStatistics() {
260 0 : base::MutexGuard guard(&mutex_);
261 0 : if (compilation_stats_ == nullptr) {
262 0 : compilation_stats_.reset(new CompilationStatistics());
263 : }
264 0 : return compilation_stats_.get();
265 : }
266 :
267 0 : void WasmEngine::DumpAndResetTurboStatistics() {
268 0 : base::MutexGuard guard(&mutex_);
269 0 : if (compilation_stats_ != nullptr) {
270 0 : StdoutStream os;
271 0 : os << AsPrintableStatistics{*compilation_stats_.get(), false} << std::endl;
272 : }
273 : compilation_stats_.reset();
274 0 : }
275 :
276 0 : CodeTracer* WasmEngine::GetCodeTracer() {
277 0 : base::MutexGuard guard(&mutex_);
278 0 : if (code_tracer_ == nullptr) code_tracer_.reset(new CodeTracer(-1));
279 0 : return code_tracer_.get();
280 : }
281 :
282 2685 : AsyncCompileJob* WasmEngine::CreateAsyncCompileJob(
283 : Isolate* isolate, const WasmFeatures& enabled,
284 : std::unique_ptr<byte[]> bytes_copy, size_t length, Handle<Context> context,
285 : std::shared_ptr<CompilationResultResolver> resolver) {
286 : AsyncCompileJob* job =
287 : new AsyncCompileJob(isolate, enabled, std::move(bytes_copy), length,
288 10740 : context, std::move(resolver));
289 : // Pass ownership to the unique_ptr in {jobs_}.
290 2685 : base::MutexGuard guard(&mutex_);
291 2685 : jobs_[job] = std::unique_ptr<AsyncCompileJob>(job);
292 5370 : return job;
293 : }
294 :
295 2674 : std::unique_ptr<AsyncCompileJob> WasmEngine::RemoveCompileJob(
296 : AsyncCompileJob* job) {
297 2674 : base::MutexGuard guard(&mutex_);
298 : auto item = jobs_.find(job);
299 : DCHECK(item != jobs_.end());
300 : std::unique_ptr<AsyncCompileJob> result = std::move(item->second);
301 : jobs_.erase(item);
302 2674 : return result;
303 : }
304 :
305 53798 : bool WasmEngine::HasRunningCompileJob(Isolate* isolate) {
306 53798 : base::MutexGuard guard(&mutex_);
307 : DCHECK_EQ(1, isolates_.count(isolate));
308 107596 : for (auto& entry : jobs_) {
309 3398 : if (entry.first->isolate() == isolate) return true;
310 : }
311 : return false;
312 : }
313 :
314 62877 : void WasmEngine::DeleteCompileJobsOnIsolate(Isolate* isolate) {
315 62877 : base::MutexGuard guard(&mutex_);
316 : DCHECK_EQ(1, isolates_.count(isolate));
317 125771 : for (auto it = jobs_.begin(); it != jobs_.end();) {
318 15 : if (it->first->isolate() == isolate) {
319 : it = jobs_.erase(it);
320 : } else {
321 : ++it;
322 : }
323 : }
324 62878 : }
325 :
326 62883 : void WasmEngine::AddIsolate(Isolate* isolate) {
327 62883 : base::MutexGuard guard(&mutex_);
328 : DCHECK_EQ(0, isolates_.count(isolate));
329 : isolates_.insert(isolate);
330 62883 : }
331 :
332 62868 : void WasmEngine::RemoveIsolate(Isolate* isolate) {
333 62868 : base::MutexGuard guard(&mutex_);
334 : DCHECK_EQ(1, isolates_.count(isolate));
335 : isolates_.erase(isolate);
336 62868 : }
337 :
338 : namespace {
339 :
340 155946 : DEFINE_LAZY_LEAKY_OBJECT_GETTER(std::shared_ptr<WasmEngine>,
341 : GetSharedWasmEngine);
342 :
343 : } // namespace
344 :
345 : // static
346 61291 : void WasmEngine::InitializeOncePerProcess() {
347 122582 : if (!FLAG_wasm_shared_engine) return;
348 122582 : *GetSharedWasmEngine() = std::make_shared<WasmEngine>();
349 : }
350 :
351 : // static
352 31878 : void WasmEngine::GlobalTearDown() {
353 63756 : if (!FLAG_wasm_shared_engine) return;
354 31878 : GetSharedWasmEngine()->reset();
355 : }
356 :
357 : // static
358 62777 : std::shared_ptr<WasmEngine> WasmEngine::GetWasmEngine() {
359 62777 : if (FLAG_wasm_shared_engine) return *GetSharedWasmEngine();
360 : return std::make_shared<WasmEngine>();
361 : }
362 :
363 : // {max_mem_pages} is declared in wasm-limits.h.
364 2007389 : uint32_t max_mem_pages() {
365 : STATIC_ASSERT(kV8MaxWasmMemoryPages <= kMaxUInt32);
366 4014778 : return std::min(uint32_t{kV8MaxWasmMemoryPages}, FLAG_wasm_max_mem_pages);
367 : }
368 :
369 : } // namespace wasm
370 : } // namespace internal
371 183867 : } // namespace v8
|