• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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