Home | History | Annotate | Download | only in wasm
      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 }
     28 
     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 
     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 
     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 
     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 
     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 
    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 
    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 
    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 
    183 std::shared_ptr<NativeModule> WasmEngine::ExportNativeModule(
    184     Handle<WasmModuleObject> module_object) {
    185   return module_object->managed_native_module()->get();
    186 }
    187 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 {
    272   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 
    287 void WasmEngine::InitializeOncePerProcess() {
    288   if (!FLAG_wasm_shared_engine) return;
    289   global_wasm_engine.Pointer()->reset(new WasmEngine());
    290 }
    291 
    292 void WasmEngine::GlobalTearDown() {
    293   if (!FLAG_wasm_shared_engine) return;
    294   global_wasm_engine.Pointer()->reset();
    295 }
    296 
    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