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