1 // Copyright 2017 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/module-compiler.h" 6 7 #include "src/api.h" 8 #include "src/asmjs/asm-js.h" 9 #include "src/base/optional.h" 10 #include "src/base/template-utils.h" 11 #include "src/base/utils/random-number-generator.h" 12 #include "src/compiler/wasm-compiler.h" 13 #include "src/counters.h" 14 #include "src/identity-map.h" 15 #include "src/property-descriptor.h" 16 #include "src/tracing/trace-event.h" 17 #include "src/trap-handler/trap-handler.h" 18 #include "src/wasm/module-decoder.h" 19 #include "src/wasm/streaming-decoder.h" 20 #include "src/wasm/wasm-code-manager.h" 21 #include "src/wasm/wasm-engine.h" 22 #include "src/wasm/wasm-js.h" 23 #include "src/wasm/wasm-limits.h" 24 #include "src/wasm/wasm-memory.h" 25 #include "src/wasm/wasm-objects-inl.h" 26 #include "src/wasm/wasm-result.h" 27 28 #define TRACE(...) \ 29 do { \ 30 if (FLAG_trace_wasm_instances) PrintF(__VA_ARGS__); \ 31 } while (false) 32 33 #define TRACE_COMPILE(...) \ 34 do { \ 35 if (FLAG_trace_wasm_compiler) PrintF(__VA_ARGS__); \ 36 } while (false) 37 38 #define TRACE_STREAMING(...) \ 39 do { \ 40 if (FLAG_trace_wasm_streaming) PrintF(__VA_ARGS__); \ 41 } while (false) 42 43 #define TRACE_LAZY(...) \ 44 do { \ 45 if (FLAG_trace_wasm_lazy_compilation) PrintF(__VA_ARGS__); \ 46 } while (false) 47 48 namespace v8 { 49 namespace internal { 50 namespace wasm { 51 52 enum class CompilationEvent : uint8_t { 53 kFinishedBaselineCompilation, 54 kFinishedTopTierCompilation, 55 kFailedCompilation 56 }; 57 58 enum class CompileMode : uint8_t { kRegular, kTiering }; 59 60 // The CompilationState keeps track of the compilation state of the 61 // owning NativeModule, i.e. which functions are left to be compiled. 62 // It contains a task manager to allow parallel and asynchronous background 63 // compilation of functions. 64 class CompilationState { 65 public: 66 CompilationState(internal::Isolate*, const ModuleEnv&); 67 ~CompilationState(); 68 69 // Set the number of compilations unit expected to be executed. Needs to be 70 // set before {AddCompilationUnits} is run, which triggers background 71 // compilation. 72 void SetNumberOfFunctionsToCompile(size_t num_functions); 73 74 // Set the callback function to be called on compilation events. Needs to be 75 // set before {AddCompilationUnits} is run. 76 void SetCallback( 77 std::function<void(CompilationEvent, ErrorThrower*)> callback); 78 79 // Inserts new functions to compile and kicks off compilation. 80 void AddCompilationUnits( 81 std::vector<std::unique_ptr<WasmCompilationUnit>>& baseline_units, 82 std::vector<std::unique_ptr<WasmCompilationUnit>>& tiering_units); 83 std::unique_ptr<WasmCompilationUnit> GetNextCompilationUnit(); 84 std::unique_ptr<WasmCompilationUnit> GetNextExecutedUnit(); 85 86 bool HasCompilationUnitToFinish(); 87 88 void OnError(ErrorThrower* thrower); 89 void OnFinishedUnit(); 90 void ScheduleUnitForFinishing(std::unique_ptr<WasmCompilationUnit> unit, 91 ExecutionTier mode); 92 93 void OnBackgroundTaskStopped(const WasmFeatures& detected); 94 void PublishDetectedFeatures(Isolate* isolate, const WasmFeatures& detected); 95 void RestartBackgroundTasks(size_t max = std::numeric_limits<size_t>::max()); 96 // Only one foreground thread (finisher) is allowed to run at a time. 97 // {SetFinisherIsRunning} returns whether the flag changed its state. 98 bool SetFinisherIsRunning(bool value); 99 void ScheduleFinisherTask(); 100 101 void Abort(); 102 103 Isolate* isolate() const { return isolate_; } 104 105 bool failed() const { 106 base::LockGuard<base::Mutex> guard(&mutex_); 107 return failed_; 108 } 109 110 bool baseline_compilation_finished() const { 111 return baseline_compilation_finished_; 112 } 113 114 WasmEngine* wasm_engine() const { return wasm_engine_; } 115 CompileMode compile_mode() const { return compile_mode_; } 116 ModuleEnv* module_env() { return &module_env_; } 117 WasmFeatures* detected_features() { return &detected_features_; } 118 119 private: 120 void NotifyOnEvent(CompilationEvent event, ErrorThrower* thrower); 121 122 std::vector<std::unique_ptr<WasmCompilationUnit>>& finish_units() { 123 return baseline_compilation_finished_ ? tiering_finish_units_ 124 : baseline_finish_units_; 125 } 126 127 // TODO(7423): Get rid of the Isolate field to make sure the CompilationState 128 // can be shared across multiple Isolates. 129 Isolate* const isolate_; 130 WasmEngine* const wasm_engine_; 131 // TODO(clemensh): Remove ModuleEnv, generate it when needed. 132 ModuleEnv module_env_; 133 const CompileMode compile_mode_; 134 bool baseline_compilation_finished_ = false; 135 136 // This mutex protects all information of this CompilationState which is being 137 // accessed concurrently. 138 mutable base::Mutex mutex_; 139 140 ////////////////////////////////////////////////////////////////////////////// 141 // Protected by {mutex_}: 142 143 std::vector<std::unique_ptr<WasmCompilationUnit>> baseline_compilation_units_; 144 std::vector<std::unique_ptr<WasmCompilationUnit>> tiering_compilation_units_; 145 146 bool finisher_is_running_ = false; 147 bool failed_ = false; 148 size_t num_background_tasks_ = 0; 149 150 std::vector<std::unique_ptr<WasmCompilationUnit>> baseline_finish_units_; 151 std::vector<std::unique_ptr<WasmCompilationUnit>> tiering_finish_units_; 152 153 // Features detected to be used in this module. Features can be detected 154 // as a module is being compiled. 155 WasmFeatures detected_features_ = kNoWasmFeatures; 156 157 // End of fields protected by {mutex_}. 158 ////////////////////////////////////////////////////////////////////////////// 159 160 // Callback function to be called on compilation events. 161 std::function<void(CompilationEvent, ErrorThrower*)> callback_; 162 163 CancelableTaskManager background_task_manager_; 164 CancelableTaskManager foreground_task_manager_; 165 std::shared_ptr<v8::TaskRunner> foreground_task_runner_; 166 167 const size_t max_background_tasks_ = 0; 168 169 size_t outstanding_units_ = 0; 170 size_t num_tiering_units_ = 0; 171 }; 172 173 namespace { 174 175 void UpdateFeatureUseCounts(Isolate* isolate, const WasmFeatures& detected) { 176 if (detected.threads) { 177 isolate->CountUsage(v8::Isolate::UseCounterFeature::kWasmThreadOpcodes); 178 } 179 } 180 181 class JSToWasmWrapperCache { 182 public: 183 Handle<Code> GetOrCompileJSToWasmWrapper(Isolate* isolate, 184 const NativeModule* native_module, 185 uint32_t func_index, 186 UseTrapHandler use_trap_handler) { 187 const WasmModule* module = native_module->module(); 188 const WasmFunction* func = &module->functions[func_index]; 189 bool is_import = func_index < module->num_imported_functions; 190 std::pair<bool, FunctionSig> key(is_import, *func->sig); 191 Handle<Code>& cached = cache_[key]; 192 if (!cached.is_null()) return cached; 193 194 Handle<Code> code = 195 compiler::CompileJSToWasmWrapper(isolate, native_module, func->sig, 196 is_import, use_trap_handler) 197 .ToHandleChecked(); 198 cached = code; 199 return code; 200 } 201 202 private: 203 // We generate different code for calling imports than calling wasm functions 204 // in this module. Both are cached separately. 205 using CacheKey = std::pair<bool, FunctionSig>; 206 std::unordered_map<CacheKey, Handle<Code>, base::hash<CacheKey>> cache_; 207 }; 208 209 // A helper class to simplify instantiating a module from a module object. 210 // It closes over the {Isolate}, the {ErrorThrower}, etc. 211 class InstanceBuilder { 212 public: 213 InstanceBuilder(Isolate* isolate, ErrorThrower* thrower, 214 Handle<WasmModuleObject> module_object, 215 MaybeHandle<JSReceiver> ffi, 216 MaybeHandle<JSArrayBuffer> memory); 217 218 // Build an instance, in all of its glory. 219 MaybeHandle<WasmInstanceObject> Build(); 220 // Run the start function, if any. 221 bool ExecuteStartFunction(); 222 223 private: 224 // Represents the initialized state of a table. 225 struct TableInstance { 226 Handle<WasmTableObject> table_object; // WebAssembly.Table instance 227 Handle<FixedArray> js_wrappers; // JSFunctions exported 228 size_t table_size; 229 }; 230 231 // A pre-evaluated value to use in import binding. 232 struct SanitizedImport { 233 Handle<String> module_name; 234 Handle<String> import_name; 235 Handle<Object> value; 236 }; 237 238 Isolate* isolate_; 239 const WasmFeatures enabled_; 240 const WasmModule* const module_; 241 ErrorThrower* thrower_; 242 Handle<WasmModuleObject> module_object_; 243 MaybeHandle<JSReceiver> ffi_; 244 MaybeHandle<JSArrayBuffer> memory_; 245 Handle<JSArrayBuffer> globals_; 246 std::vector<TableInstance> table_instances_; 247 std::vector<Handle<JSFunction>> js_wrappers_; 248 Handle<WasmExportedFunction> start_function_; 249 JSToWasmWrapperCache js_to_wasm_cache_; 250 std::vector<SanitizedImport> sanitized_imports_; 251 252 UseTrapHandler use_trap_handler() const { 253 return module_object_->native_module()->use_trap_handler() ? kUseTrapHandler 254 : kNoTrapHandler; 255 } 256 257 // Helper routines to print out errors with imports. 258 #define ERROR_THROWER_WITH_MESSAGE(TYPE) \ 259 void Report##TYPE(const char* error, uint32_t index, \ 260 Handle<String> module_name, Handle<String> import_name) { \ 261 thrower_->TYPE("Import #%d module=\"%s\" function=\"%s\" error: %s", \ 262 index, module_name->ToCString().get(), \ 263 import_name->ToCString().get(), error); \ 264 } \ 265 \ 266 MaybeHandle<Object> Report##TYPE(const char* error, uint32_t index, \ 267 Handle<String> module_name) { \ 268 thrower_->TYPE("Import #%d module=\"%s\" error: %s", index, \ 269 module_name->ToCString().get(), error); \ 270 return MaybeHandle<Object>(); \ 271 } 272 273 ERROR_THROWER_WITH_MESSAGE(LinkError) 274 ERROR_THROWER_WITH_MESSAGE(TypeError) 275 276 #undef ERROR_THROWER_WITH_MESSAGE 277 278 // Look up an import value in the {ffi_} object. 279 MaybeHandle<Object> LookupImport(uint32_t index, Handle<String> module_name, 280 Handle<String> import_name); 281 282 // Look up an import value in the {ffi_} object specifically for linking an 283 // asm.js module. This only performs non-observable lookups, which allows 284 // falling back to JavaScript proper (and hence re-executing all lookups) if 285 // module instantiation fails. 286 MaybeHandle<Object> LookupImportAsm(uint32_t index, 287 Handle<String> import_name); 288 289 uint32_t EvalUint32InitExpr(const WasmInitExpr& expr); 290 291 // Load data segments into the memory. 292 void LoadDataSegments(Handle<WasmInstanceObject> instance); 293 294 void WriteGlobalValue(const WasmGlobal& global, double value); 295 void WriteGlobalValue(const WasmGlobal& global, 296 Handle<WasmGlobalObject> value); 297 298 void SanitizeImports(); 299 300 // Find the imported memory buffer if there is one. This is used to see if we 301 // need to recompile with bounds checks before creating the instance. 302 MaybeHandle<JSArrayBuffer> FindImportedMemoryBuffer() const; 303 304 // Process the imports, including functions, tables, globals, and memory, in 305 // order, loading them from the {ffi_} object. Returns the number of imported 306 // functions. 307 int ProcessImports(Handle<WasmInstanceObject> instance); 308 309 template <typename T> 310 T* GetRawGlobalPtr(const WasmGlobal& global); 311 312 // Process initialization of globals. 313 void InitGlobals(); 314 315 // Allocate memory for a module instance as a new JSArrayBuffer. 316 Handle<JSArrayBuffer> AllocateMemory(uint32_t num_pages); 317 318 bool NeedsWrappers() const; 319 320 // Process the exports, creating wrappers for functions, tables, memories, 321 // and globals. 322 void ProcessExports(Handle<WasmInstanceObject> instance); 323 324 void InitializeTables(Handle<WasmInstanceObject> instance); 325 326 void LoadTableSegments(Handle<WasmInstanceObject> instance); 327 }; 328 329 } // namespace 330 331 MaybeHandle<WasmInstanceObject> InstantiateToInstanceObject( 332 Isolate* isolate, ErrorThrower* thrower, 333 Handle<WasmModuleObject> module_object, MaybeHandle<JSReceiver> imports, 334 MaybeHandle<JSArrayBuffer> memory) { 335 InstanceBuilder builder(isolate, thrower, module_object, imports, memory); 336 auto instance = builder.Build(); 337 if (!instance.is_null() && builder.ExecuteStartFunction()) { 338 return instance; 339 } 340 return {}; 341 } 342 343 WasmCode* LazyCompileFunction(Isolate* isolate, NativeModule* native_module, 344 int func_index) { 345 base::ElapsedTimer compilation_timer; 346 DCHECK(!native_module->has_code(static_cast<uint32_t>(func_index))); 347 348 compilation_timer.Start(); 349 350 ModuleEnv* module_env = native_module->compilation_state()->module_env(); 351 // TODO(wasm): Refactor this to only get the name if it is really needed for 352 // tracing / debugging. 353 WasmName func_name; 354 { 355 ModuleWireBytes wire_bytes(native_module->wire_bytes()); 356 WireBytesRef name_ref = 357 module_env->module->LookupFunctionName(wire_bytes, func_index); 358 func_name = wire_bytes.GetName(name_ref); 359 } 360 361 TRACE_LAZY("Compiling function '%.*s' (#%d).\n", func_name.length(), 362 func_name.start(), func_index); 363 364 const uint8_t* module_start = native_module->wire_bytes().start(); 365 366 const WasmFunction* func = &module_env->module->functions[func_index]; 367 FunctionBody body{func->sig, func->code.offset(), 368 module_start + func->code.offset(), 369 module_start + func->code.end_offset()}; 370 371 ErrorThrower thrower(isolate, "WasmLazyCompile"); 372 WasmCompilationUnit unit(isolate->wasm_engine(), module_env, native_module, 373 body, func_name, func_index, isolate->counters()); 374 unit.ExecuteCompilation( 375 native_module->compilation_state()->detected_features()); 376 WasmCode* wasm_code = unit.FinishCompilation(&thrower); 377 378 if (WasmCode::ShouldBeLogged(isolate)) wasm_code->LogCode(isolate); 379 380 // If there is a pending error, something really went wrong. The module was 381 // verified before starting execution with lazy compilation. 382 // This might be OOM, but then we cannot continue execution anyway. 383 // TODO(clemensh): According to the spec, we can actually skip validation at 384 // module creation time, and return a function that always traps here. 385 CHECK(!thrower.error()); 386 387 int64_t func_size = 388 static_cast<int64_t>(func->code.end_offset() - func->code.offset()); 389 int64_t compilation_time = compilation_timer.Elapsed().InMicroseconds(); 390 391 auto counters = isolate->counters(); 392 counters->wasm_lazily_compiled_functions()->Increment(); 393 394 counters->wasm_lazy_compilation_throughput()->AddSample( 395 compilation_time != 0 ? static_cast<int>(func_size / compilation_time) 396 : 0); 397 398 return wasm_code; 399 } 400 401 Address CompileLazy(Isolate* isolate, NativeModule* native_module, 402 uint32_t func_index) { 403 HistogramTimerScope lazy_time_scope( 404 isolate->counters()->wasm_lazy_compilation_time()); 405 406 DCHECK(!native_module->lazy_compile_frozen()); 407 408 NativeModuleModificationScope native_module_modification_scope(native_module); 409 410 WasmCode* result = LazyCompileFunction(isolate, native_module, func_index); 411 DCHECK_NOT_NULL(result); 412 DCHECK_EQ(func_index, result->index()); 413 414 return result->instruction_start(); 415 } 416 417 namespace { 418 bool compile_lazy(const WasmModule* module) { 419 return FLAG_wasm_lazy_compilation || 420 (FLAG_asm_wasm_lazy_compilation && module->origin == kAsmJsOrigin); 421 } 422 423 byte* raw_buffer_ptr(MaybeHandle<JSArrayBuffer> buffer, int offset) { 424 return static_cast<byte*>(buffer.ToHandleChecked()->backing_store()) + offset; 425 } 426 427 void RecordStats(const Code* code, Counters* counters) { 428 counters->wasm_generated_code_size()->Increment(code->body_size()); 429 counters->wasm_reloc_size()->Increment(code->relocation_info()->length()); 430 } 431 432 bool in_bounds(uint32_t offset, size_t size, size_t upper) { 433 return offset + size <= upper && offset + size >= offset; 434 } 435 436 using WasmInstanceMap = 437 IdentityMap<Handle<WasmInstanceObject>, FreeStoreAllocationPolicy>; 438 439 double MonotonicallyIncreasingTimeInMs() { 440 return V8::GetCurrentPlatform()->MonotonicallyIncreasingTime() * 441 base::Time::kMillisecondsPerSecond; 442 } 443 444 ModuleEnv CreateDefaultModuleEnv(const WasmModule* module, 445 bool allow_trap_handler = true) { 446 UseTrapHandler use_trap_handler = 447 trap_handler::IsTrapHandlerEnabled() && allow_trap_handler 448 ? kUseTrapHandler 449 : kNoTrapHandler; 450 return ModuleEnv(module, use_trap_handler, kRuntimeExceptionSupport); 451 } 452 453 // The CompilationUnitBuilder builds compilation units and stores them in an 454 // internal buffer. The buffer is moved into the working queue of the 455 // CompilationState when {Commit} is called. 456 class CompilationUnitBuilder { 457 public: 458 explicit CompilationUnitBuilder(NativeModule* native_module) 459 : native_module_(native_module), 460 compilation_state_(native_module->compilation_state()) {} 461 462 void AddUnit(const WasmFunction* function, uint32_t buffer_offset, 463 Vector<const uint8_t> bytes, WasmName name) { 464 switch (compilation_state_->compile_mode()) { 465 case CompileMode::kTiering: 466 tiering_units_.emplace_back(CreateUnit( 467 function, buffer_offset, bytes, name, ExecutionTier::kOptimized)); 468 baseline_units_.emplace_back(CreateUnit( 469 function, buffer_offset, bytes, name, ExecutionTier::kBaseline)); 470 return; 471 case CompileMode::kRegular: 472 baseline_units_.emplace_back( 473 CreateUnit(function, buffer_offset, bytes, name, 474 WasmCompilationUnit::GetDefaultExecutionTier())); 475 return; 476 } 477 UNREACHABLE(); 478 } 479 480 bool Commit() { 481 if (baseline_units_.empty() && tiering_units_.empty()) return false; 482 compilation_state_->AddCompilationUnits(baseline_units_, tiering_units_); 483 Clear(); 484 return true; 485 } 486 487 void Clear() { 488 baseline_units_.clear(); 489 tiering_units_.clear(); 490 } 491 492 private: 493 std::unique_ptr<WasmCompilationUnit> CreateUnit(const WasmFunction* function, 494 uint32_t buffer_offset, 495 Vector<const uint8_t> bytes, 496 WasmName name, 497 ExecutionTier mode) { 498 return base::make_unique<WasmCompilationUnit>( 499 compilation_state_->wasm_engine(), compilation_state_->module_env(), 500 native_module_, 501 FunctionBody{function->sig, buffer_offset, bytes.begin(), bytes.end()}, 502 name, function->func_index, 503 compilation_state_->isolate()->async_counters().get(), mode); 504 } 505 506 NativeModule* native_module_; 507 CompilationState* compilation_state_; 508 std::vector<std::unique_ptr<WasmCompilationUnit>> baseline_units_; 509 std::vector<std::unique_ptr<WasmCompilationUnit>> tiering_units_; 510 }; 511 512 // Run by each compilation task and by the main thread (i.e. in both 513 // foreground and background threads). The no_finisher_callback is called 514 // within the result_mutex_ lock when no finishing task is running, i.e. when 515 // the finisher_is_running_ flag is not set. 516 bool FetchAndExecuteCompilationUnit(CompilationState* compilation_state, 517 WasmFeatures* detected) { 518 DisallowHeapAccess no_heap_access; 519 520 std::unique_ptr<WasmCompilationUnit> unit = 521 compilation_state->GetNextCompilationUnit(); 522 if (unit == nullptr) return false; 523 524 // TODO(kimanh): We need to find out in which mode the unit 525 // should be compiled in before compiling it, as it might fallback 526 // to Turbofan if it cannot be compiled using Liftoff. This can be removed 527 // later as soon as Liftoff can compile any function. Then, we can directly 528 // access {unit->mode()} within {ScheduleUnitForFinishing()}. 529 ExecutionTier mode = unit->mode(); 530 unit->ExecuteCompilation(detected); 531 compilation_state->ScheduleUnitForFinishing(std::move(unit), mode); 532 533 return true; 534 } 535 536 void InitializeCompilationUnits(NativeModule* native_module) { 537 ModuleWireBytes wire_bytes(native_module->wire_bytes()); 538 const WasmModule* module = native_module->module(); 539 CompilationUnitBuilder builder(native_module); 540 uint32_t start = module->num_imported_functions; 541 uint32_t end = start + module->num_declared_functions; 542 for (uint32_t i = start; i < end; ++i) { 543 const WasmFunction* func = &module->functions[i]; 544 uint32_t buffer_offset = func->code.offset(); 545 Vector<const uint8_t> bytes(wire_bytes.start() + func->code.offset(), 546 func->code.end_offset() - func->code.offset()); 547 548 WasmName name = wire_bytes.GetName(func, module); 549 DCHECK_NOT_NULL(native_module); 550 builder.AddUnit(func, buffer_offset, bytes, name); 551 } 552 builder.Commit(); 553 } 554 555 void FinishCompilationUnits(CompilationState* compilation_state, 556 ErrorThrower* thrower) { 557 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.wasm"), "FinishCompilationUnits"); 558 while (true) { 559 if (compilation_state->failed()) break; 560 std::unique_ptr<WasmCompilationUnit> unit = 561 compilation_state->GetNextExecutedUnit(); 562 if (unit == nullptr) break; 563 WasmCode* result = unit->FinishCompilation(thrower); 564 565 if (thrower->error()) { 566 compilation_state->Abort(); 567 break; 568 } 569 570 // Update the compilation state. 571 compilation_state->OnFinishedUnit(); 572 DCHECK_IMPLIES(result == nullptr, thrower->error()); 573 if (result == nullptr) break; 574 } 575 if (!compilation_state->failed()) { 576 compilation_state->RestartBackgroundTasks(); 577 } 578 } 579 580 void CompileInParallel(Isolate* isolate, NativeModule* native_module, 581 Handle<WasmModuleObject> module_object, 582 ErrorThrower* thrower) { 583 // Data structures for the parallel compilation. 584 585 //----------------------------------------------------------------------- 586 // For parallel compilation: 587 // 1) The main thread allocates a compilation unit for each wasm function 588 // and stores them in the vector {compilation_units} within the 589 // {compilation_state}. By adding units to the {compilation_state}, new 590 // {BackgroundCompileTasks} instances are spawned which run on 591 // the background threads. 592 // 2.a) The background threads and the main thread pick one compilation 593 // unit at a time and execute the parallel phase of the compilation 594 // unit. After finishing the execution of the parallel phase, the 595 // result is enqueued in {baseline_finish_units_}. 596 // 2.b) If {baseline_finish_units_} contains a compilation unit, the main 597 // thread dequeues it and finishes the compilation. 598 // 3) After the parallel phase of all compilation units has started, the 599 // main thread continues to finish all compilation units as long as 600 // baseline-compilation units are left to be processed. 601 // 4) If tier-up is enabled, the main thread restarts background tasks 602 // that take care of compiling and finishing the top-tier compilation 603 // units. 604 605 // Turn on the {CanonicalHandleScope} so that the background threads can 606 // use the node cache. 607 CanonicalHandleScope canonical(isolate); 608 609 CompilationState* compilation_state = native_module->compilation_state(); 610 // Make sure that no foreground task is spawned for finishing 611 // the compilation units. This foreground thread will be 612 // responsible for finishing compilation. 613 compilation_state->SetFinisherIsRunning(true); 614 uint32_t num_wasm_functions = 615 native_module->num_functions() - native_module->num_imported_functions(); 616 compilation_state->SetNumberOfFunctionsToCompile(num_wasm_functions); 617 618 // 1) The main thread allocates a compilation unit for each wasm function 619 // and stores them in the vector {compilation_units} within the 620 // {compilation_state}. By adding units to the {compilation_state}, new 621 // {BackgroundCompileTask} instances are spawned which run on 622 // background threads. 623 InitializeCompilationUnits(native_module); 624 625 // 2.a) The background threads and the main thread pick one compilation 626 // unit at a time and execute the parallel phase of the compilation 627 // unit. After finishing the execution of the parallel phase, the 628 // result is enqueued in {baseline_finish_units_}. 629 // The foreground task bypasses waiting on memory threshold, because 630 // its results will immediately be converted to code (below). 631 WasmFeatures detected_features; 632 while ( 633 FetchAndExecuteCompilationUnit(compilation_state, &detected_features) && 634 !compilation_state->baseline_compilation_finished()) { 635 // 2.b) If {baseline_finish_units_} contains a compilation unit, the main 636 // thread dequeues it and finishes the compilation unit. Compilation 637 // units are finished concurrently to the background threads to save 638 // memory. 639 FinishCompilationUnits(compilation_state, thrower); 640 641 if (compilation_state->failed()) break; 642 } 643 644 while (!compilation_state->failed()) { 645 // 3) After the parallel phase of all compilation units has started, the 646 // main thread continues to finish compilation units as long as 647 // baseline compilation units are left to be processed. If compilation 648 // already failed, all background tasks have already been canceled 649 // in {FinishCompilationUnits}, and there are no units to finish. 650 FinishCompilationUnits(compilation_state, thrower); 651 652 if (compilation_state->baseline_compilation_finished()) break; 653 } 654 655 // Publish features from the foreground and background tasks. 656 compilation_state->PublishDetectedFeatures(isolate, detected_features); 657 658 // 4) If tiering-compilation is enabled, we need to set the finisher 659 // to false, such that the background threads will spawn a foreground 660 // thread to finish the top-tier compilation units. 661 if (!compilation_state->failed() && 662 compilation_state->compile_mode() == CompileMode::kTiering) { 663 compilation_state->SetFinisherIsRunning(false); 664 compilation_state->RestartBackgroundTasks(); 665 } 666 } 667 668 void CompileSequentially(Isolate* isolate, NativeModule* native_module, 669 ModuleEnv* module_env, ErrorThrower* thrower) { 670 DCHECK(!thrower->error()); 671 672 ModuleWireBytes wire_bytes(native_module->wire_bytes()); 673 const WasmModule* module = module_env->module; 674 WasmFeatures detected = kNoWasmFeatures; 675 for (uint32_t i = 0; i < module->functions.size(); ++i) { 676 const WasmFunction& func = module->functions[i]; 677 if (func.imported) continue; // Imports are compiled at instantiation time. 678 679 // Compile the function. 680 WasmCode* code = WasmCompilationUnit::CompileWasmFunction( 681 isolate, native_module, &detected, thrower, module_env, &func); 682 if (code == nullptr) { 683 TruncatedUserString<> name(wire_bytes.GetName(&func, module)); 684 thrower->CompileError("Compilation of #%d:%.*s failed.", i, name.length(), 685 name.start()); 686 break; 687 } 688 } 689 UpdateFeatureUseCounts(isolate, detected); 690 } 691 692 void ValidateSequentially(Isolate* isolate, NativeModule* native_module, 693 ErrorThrower* thrower) { 694 DCHECK(!thrower->error()); 695 696 ModuleWireBytes wire_bytes(native_module->wire_bytes()); 697 const WasmModule* module = native_module->module(); 698 uint32_t start = module->num_imported_functions; 699 uint32_t end = start + module->num_declared_functions; 700 for (uint32_t i = start; i < end; ++i) { 701 const WasmFunction& func = module->functions[i]; 702 703 const byte* base = wire_bytes.start(); 704 FunctionBody body{func.sig, func.code.offset(), base + func.code.offset(), 705 base + func.code.end_offset()}; 706 DecodeResult result; 707 { 708 auto time_counter = 709 SELECT_WASM_COUNTER(isolate->async_counters(), module->origin, 710 wasm_decode, function_time); 711 712 TimedHistogramScope wasm_decode_function_time_scope(time_counter); 713 WasmFeatures detected; 714 result = VerifyWasmCode(isolate->allocator(), 715 native_module->enabled_features(), module, 716 &detected, body); 717 } 718 if (result.failed()) { 719 TruncatedUserString<> name(wire_bytes.GetName(&func, module)); 720 thrower->CompileError("Compiling function #%d:%.*s failed: %s @+%u", i, 721 name.length(), name.start(), 722 result.error_msg().c_str(), result.error_offset()); 723 break; 724 } 725 } 726 } 727 728 void CompileNativeModule(Isolate* isolate, ErrorThrower* thrower, 729 Handle<WasmModuleObject> module_object, 730 const WasmModule* wasm_module, ModuleEnv* env) { 731 NativeModule* const native_module = module_object->native_module(); 732 ModuleWireBytes wire_bytes(native_module->wire_bytes()); 733 734 if (compile_lazy(wasm_module)) { 735 if (wasm_module->origin == kWasmOrigin) { 736 // Validate wasm modules for lazy compilation. Don't validate asm.js 737 // modules, they are valid by construction (otherwise a CHECK will fail 738 // during lazy compilation). 739 // TODO(clemensh): According to the spec, we can actually skip validation 740 // at module creation time, and return a function that always traps at 741 // (lazy) compilation time. 742 ValidateSequentially(isolate, native_module, thrower); 743 if (thrower->error()) return; 744 } 745 746 native_module->SetLazyBuiltin(BUILTIN_CODE(isolate, WasmCompileLazy)); 747 } else { 748 size_t funcs_to_compile = 749 wasm_module->functions.size() - wasm_module->num_imported_functions; 750 bool compile_parallel = 751 !FLAG_trace_wasm_decoder && FLAG_wasm_num_compilation_tasks > 0 && 752 funcs_to_compile > 1 && 753 V8::GetCurrentPlatform()->NumberOfWorkerThreads() > 0; 754 755 if (compile_parallel) { 756 CompileInParallel(isolate, native_module, module_object, thrower); 757 } else { 758 CompileSequentially(isolate, native_module, env, thrower); 759 } 760 if (thrower->error()) return; 761 } 762 } 763 764 // The runnable task that finishes compilation in foreground (e.g. updating 765 // the NativeModule, the code table, etc.). 766 class FinishCompileTask : public CancelableTask { 767 public: 768 explicit FinishCompileTask(CompilationState* compilation_state, 769 CancelableTaskManager* task_manager) 770 : CancelableTask(task_manager), compilation_state_(compilation_state) {} 771 772 void RunInternal() override { 773 Isolate* isolate = compilation_state_->isolate(); 774 HandleScope scope(isolate); 775 SaveContext saved_context(isolate); 776 isolate->set_context(nullptr); 777 778 TRACE_COMPILE("(4a) Finishing compilation units...\n"); 779 if (compilation_state_->failed()) { 780 compilation_state_->SetFinisherIsRunning(false); 781 return; 782 } 783 784 // We execute for 1 ms and then reschedule the task, same as the GC. 785 double deadline = MonotonicallyIncreasingTimeInMs() + 1.0; 786 while (true) { 787 compilation_state_->RestartBackgroundTasks(); 788 789 std::unique_ptr<WasmCompilationUnit> unit = 790 compilation_state_->GetNextExecutedUnit(); 791 792 if (unit == nullptr) { 793 // It might happen that a background task just scheduled a unit to be 794 // finished, but did not start a finisher task since the flag was still 795 // set. Check for this case, and continue if there is more work. 796 compilation_state_->SetFinisherIsRunning(false); 797 if (compilation_state_->HasCompilationUnitToFinish() && 798 compilation_state_->SetFinisherIsRunning(true)) { 799 continue; 800 } 801 break; 802 } 803 804 ErrorThrower thrower(compilation_state_->isolate(), "AsyncCompile"); 805 WasmCode* result = unit->FinishCompilation(&thrower); 806 807 if (thrower.error()) { 808 DCHECK_NULL(result); 809 compilation_state_->OnError(&thrower); 810 compilation_state_->SetFinisherIsRunning(false); 811 thrower.Reset(); 812 break; 813 } 814 815 if (compilation_state_->baseline_compilation_finished()) { 816 // If Liftoff compilation finishes it will directly start executing. 817 // As soon as we have Turbofan-compiled code available, it will 818 // directly be used by Liftoff-compiled code via the jump table. 819 DCHECK_EQ(CompileMode::kTiering, compilation_state_->compile_mode()); 820 DCHECK(!result->is_liftoff()); 821 822 if (WasmCode::ShouldBeLogged(isolate)) result->LogCode(isolate); 823 } 824 825 // Update the compilation state, and possibly notify 826 // threads waiting for events. 827 compilation_state_->OnFinishedUnit(); 828 829 if (deadline < MonotonicallyIncreasingTimeInMs()) { 830 // We reached the deadline. We reschedule this task and return 831 // immediately. Since we rescheduled this task already, we do not set 832 // the FinisherIsRunning flag to false. 833 compilation_state_->ScheduleFinisherTask(); 834 return; 835 } 836 } 837 } 838 839 private: 840 CompilationState* compilation_state_; 841 }; 842 843 // The runnable task that performs compilations in the background. 844 class BackgroundCompileTask : public CancelableTask { 845 public: 846 explicit BackgroundCompileTask(CompilationState* compilation_state, 847 CancelableTaskManager* task_manager) 848 : CancelableTask(task_manager), compilation_state_(compilation_state) {} 849 850 void RunInternal() override { 851 TRACE_COMPILE("(3b) Compiling...\n"); 852 // The number of currently running background tasks is reduced in 853 // {OnBackgroundTaskStopped}. 854 while (!compilation_state_->failed()) { 855 if (!FetchAndExecuteCompilationUnit(compilation_state_, 856 &detected_features_)) { 857 break; 858 } 859 } 860 compilation_state_->OnBackgroundTaskStopped(detected_features_); 861 } 862 863 private: 864 CompilationState* compilation_state_; 865 WasmFeatures detected_features_ = kNoWasmFeatures; 866 }; 867 } // namespace 868 869 MaybeHandle<WasmModuleObject> CompileToModuleObject( 870 Isolate* isolate, const WasmFeatures& enabled, ErrorThrower* thrower, 871 std::shared_ptr<const WasmModule> module, const ModuleWireBytes& wire_bytes, 872 Handle<Script> asm_js_script, 873 Vector<const byte> asm_js_offset_table_bytes) { 874 const WasmModule* wasm_module = module.get(); 875 TimedHistogramScope wasm_compile_module_time_scope(SELECT_WASM_COUNTER( 876 isolate->counters(), wasm_module->origin, wasm_compile, module_time)); 877 878 // Embedder usage count for declared shared memories. 879 if (wasm_module->has_shared_memory) { 880 isolate->CountUsage(v8::Isolate::UseCounterFeature::kWasmSharedMemory); 881 } 882 883 // TODO(6792): No longer needed once WebAssembly code is off heap. Use 884 // base::Optional to be able to close the scope before notifying the debugger. 885 base::Optional<CodeSpaceMemoryModificationScope> modification_scope( 886 base::in_place_t(), isolate->heap()); 887 888 // Create heap objects for script, module bytes and asm.js offset table to 889 // be stored in the module object. 890 Handle<Script> script; 891 Handle<ByteArray> asm_js_offset_table; 892 if (asm_js_script.is_null()) { 893 script = CreateWasmScript(isolate, wire_bytes); 894 } else { 895 script = asm_js_script; 896 asm_js_offset_table = 897 isolate->factory()->NewByteArray(asm_js_offset_table_bytes.length()); 898 asm_js_offset_table->copy_in(0, asm_js_offset_table_bytes.start(), 899 asm_js_offset_table_bytes.length()); 900 } 901 // TODO(wasm): only save the sections necessary to deserialize a 902 // {WasmModule}. E.g. function bodies could be omitted. 903 OwnedVector<uint8_t> wire_bytes_copy = 904 OwnedVector<uint8_t>::Of(wire_bytes.module_bytes()); 905 906 // Create the module object. 907 // TODO(clemensh): For the same module (same bytes / same hash), we should 908 // only have one WasmModuleObject. Otherwise, we might only set 909 // breakpoints on a (potentially empty) subset of the instances. 910 ModuleEnv env = CreateDefaultModuleEnv(wasm_module); 911 912 // Create the compiled module object and populate with compiled functions 913 // and information needed at instantiation time. This object needs to be 914 // serializable. Instantiation may occur off a deserialized version of this 915 // object. 916 Handle<WasmModuleObject> module_object = WasmModuleObject::New( 917 isolate, enabled, std::move(module), env, std::move(wire_bytes_copy), 918 script, asm_js_offset_table); 919 CompileNativeModule(isolate, thrower, module_object, wasm_module, &env); 920 if (thrower->error()) return {}; 921 922 // Compile JS->wasm wrappers for exported functions. 923 CompileJsToWasmWrappers(isolate, module_object); 924 925 // If we created a wasm script, finish it now and make it public to the 926 // debugger. 927 if (asm_js_script.is_null()) { 928 // Close the CodeSpaceMemoryModificationScope before calling into the 929 // debugger. 930 modification_scope.reset(); 931 isolate->debug()->OnAfterCompile(script); 932 } 933 934 // Log the code within the generated module for profiling. 935 module_object->native_module()->LogWasmCodes(isolate); 936 937 return module_object; 938 } 939 940 InstanceBuilder::InstanceBuilder(Isolate* isolate, ErrorThrower* thrower, 941 Handle<WasmModuleObject> module_object, 942 MaybeHandle<JSReceiver> ffi, 943 MaybeHandle<JSArrayBuffer> memory) 944 : isolate_(isolate), 945 enabled_(module_object->native_module()->enabled_features()), 946 module_(module_object->module()), 947 thrower_(thrower), 948 module_object_(module_object), 949 ffi_(ffi), 950 memory_(memory) { 951 sanitized_imports_.reserve(module_->import_table.size()); 952 } 953 954 // Build an instance, in all of its glory. 955 MaybeHandle<WasmInstanceObject> InstanceBuilder::Build() { 956 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.wasm"), "InstanceBuilder::Build"); 957 // Check that an imports argument was provided, if the module requires it. 958 // No point in continuing otherwise. 959 if (!module_->import_table.empty() && ffi_.is_null()) { 960 thrower_->TypeError( 961 "Imports argument must be present and must be an object"); 962 return {}; 963 } 964 965 SanitizeImports(); 966 if (thrower_->error()) return {}; 967 968 // TODO(6792): No longer needed once WebAssembly code is off heap. 969 CodeSpaceMemoryModificationScope modification_scope(isolate_->heap()); 970 // From here on, we expect the build pipeline to run without exiting to JS. 971 DisallowJavascriptExecution no_js(isolate_); 972 // Record build time into correct bucket, then build instance. 973 TimedHistogramScope wasm_instantiate_module_time_scope(SELECT_WASM_COUNTER( 974 isolate_->counters(), module_->origin, wasm_instantiate, module_time)); 975 976 //-------------------------------------------------------------------------- 977 // Allocate the memory array buffer. 978 //-------------------------------------------------------------------------- 979 // We allocate the memory buffer before cloning or reusing the compiled module 980 // so we will know whether we need to recompile with bounds checks. 981 uint32_t initial_pages = module_->initial_pages; 982 auto initial_pages_counter = SELECT_WASM_COUNTER( 983 isolate_->counters(), module_->origin, wasm, min_mem_pages_count); 984 initial_pages_counter->AddSample(initial_pages); 985 // Asm.js has memory_ already set at this point, so we don't want to 986 // overwrite it. 987 if (memory_.is_null()) { 988 memory_ = FindImportedMemoryBuffer(); 989 } 990 if (!memory_.is_null()) { 991 // Set externally passed ArrayBuffer non neuterable. 992 Handle<JSArrayBuffer> memory = memory_.ToHandleChecked(); 993 memory->set_is_neuterable(false); 994 995 DCHECK_IMPLIES(use_trap_handler(), module_->origin == kAsmJsOrigin || 996 memory->is_wasm_memory() || 997 memory->backing_store() == nullptr); 998 } else if (initial_pages > 0 || use_trap_handler()) { 999 // We need to unconditionally create a guard region if using trap handlers, 1000 // even when the size is zero to prevent null-dereference issues 1001 // (e.g. https://crbug.com/769637). 1002 // Allocate memory if the initial size is more than 0 pages. 1003 memory_ = AllocateMemory(initial_pages); 1004 if (memory_.is_null()) return {}; // failed to allocate memory 1005 } 1006 1007 //-------------------------------------------------------------------------- 1008 // Recompile module if using trap handlers but could not get guarded memory 1009 //-------------------------------------------------------------------------- 1010 if (module_->origin == kWasmOrigin && use_trap_handler()) { 1011 // Make sure the memory has suitable guard regions. 1012 WasmMemoryTracker* const memory_tracker = 1013 isolate_->wasm_engine()->memory_tracker(); 1014 1015 if (!memory_tracker->HasFullGuardRegions( 1016 memory_.ToHandleChecked()->backing_store())) { 1017 if (!FLAG_wasm_trap_handler_fallback) { 1018 return {}; 1019 } 1020 1021 TRACE("Recompiling module without bounds checks\n"); 1022 constexpr bool allow_trap_handler = false; 1023 ModuleEnv env = CreateDefaultModuleEnv(module_, allow_trap_handler); 1024 // Disable trap handlers on this native module. 1025 NativeModule* native_module = module_object_->native_module(); 1026 native_module->DisableTrapHandler(); 1027 1028 // Recompile all functions in this native module. 1029 ErrorThrower thrower(isolate_, "recompile"); 1030 CompileNativeModule(isolate_, &thrower, module_object_, module_, &env); 1031 if (thrower.error()) { 1032 return {}; 1033 } 1034 DCHECK(!native_module->use_trap_handler()); 1035 } 1036 } 1037 1038 //-------------------------------------------------------------------------- 1039 // Create the WebAssembly.Instance object. 1040 //-------------------------------------------------------------------------- 1041 NativeModule* native_module = module_object_->native_module(); 1042 TRACE("New module instantiation for %p\n", native_module); 1043 Handle<WasmInstanceObject> instance = 1044 WasmInstanceObject::New(isolate_, module_object_); 1045 NativeModuleModificationScope native_modification_scope(native_module); 1046 1047 //-------------------------------------------------------------------------- 1048 // Set up the globals for the new instance. 1049 //-------------------------------------------------------------------------- 1050 MaybeHandle<JSArrayBuffer> old_globals; 1051 uint32_t globals_buffer_size = module_->globals_buffer_size; 1052 if (globals_buffer_size > 0) { 1053 void* backing_store = 1054 isolate_->array_buffer_allocator()->Allocate(globals_buffer_size); 1055 if (backing_store == nullptr) { 1056 thrower_->RangeError("Out of memory: wasm globals"); 1057 return {}; 1058 } 1059 globals_ = 1060 isolate_->factory()->NewJSArrayBuffer(SharedFlag::kNotShared, TENURED); 1061 constexpr bool is_external = false; 1062 constexpr bool is_wasm_memory = false; 1063 JSArrayBuffer::Setup(globals_, isolate_, is_external, backing_store, 1064 globals_buffer_size, SharedFlag::kNotShared, 1065 is_wasm_memory); 1066 if (globals_.is_null()) { 1067 thrower_->RangeError("Out of memory: wasm globals"); 1068 return {}; 1069 } 1070 instance->set_globals_start( 1071 reinterpret_cast<byte*>(globals_->backing_store())); 1072 instance->set_globals_buffer(*globals_); 1073 } 1074 1075 //-------------------------------------------------------------------------- 1076 // Set up the array of references to imported globals' array buffers. 1077 //-------------------------------------------------------------------------- 1078 if (module_->num_imported_mutable_globals > 0) { 1079 // TODO(binji): This allocates one slot for each mutable global, which is 1080 // more than required if multiple globals are imported from the same 1081 // module. 1082 Handle<FixedArray> buffers_array = isolate_->factory()->NewFixedArray( 1083 module_->num_imported_mutable_globals, TENURED); 1084 instance->set_imported_mutable_globals_buffers(*buffers_array); 1085 } 1086 1087 //-------------------------------------------------------------------------- 1088 // Reserve the metadata for indirect function tables. 1089 //-------------------------------------------------------------------------- 1090 int table_count = static_cast<int>(module_->tables.size()); 1091 table_instances_.resize(table_count); 1092 1093 //-------------------------------------------------------------------------- 1094 // Process the imports for the module. 1095 //-------------------------------------------------------------------------- 1096 int num_imported_functions = ProcessImports(instance); 1097 if (num_imported_functions < 0) return {}; 1098 1099 //-------------------------------------------------------------------------- 1100 // Process the initialization for the module's globals. 1101 //-------------------------------------------------------------------------- 1102 InitGlobals(); 1103 1104 //-------------------------------------------------------------------------- 1105 // Initialize the indirect tables. 1106 //-------------------------------------------------------------------------- 1107 if (table_count > 0) { 1108 InitializeTables(instance); 1109 } 1110 1111 //-------------------------------------------------------------------------- 1112 // Create the WebAssembly.Memory object. 1113 //-------------------------------------------------------------------------- 1114 if (module_->has_memory) { 1115 if (!instance->has_memory_object()) { 1116 // No memory object exists. Create one. 1117 Handle<WasmMemoryObject> memory_object = WasmMemoryObject::New( 1118 isolate_, memory_, 1119 module_->maximum_pages != 0 ? module_->maximum_pages : -1); 1120 instance->set_memory_object(*memory_object); 1121 } 1122 1123 // Add the instance object to the list of instances for this memory. 1124 Handle<WasmMemoryObject> memory_object(instance->memory_object(), isolate_); 1125 WasmMemoryObject::AddInstance(isolate_, memory_object, instance); 1126 1127 if (!memory_.is_null()) { 1128 // Double-check the {memory} array buffer matches the instance. 1129 Handle<JSArrayBuffer> memory = memory_.ToHandleChecked(); 1130 CHECK_EQ(instance->memory_size(), memory->byte_length()->Number()); 1131 CHECK_EQ(instance->memory_start(), memory->backing_store()); 1132 } 1133 } 1134 1135 //-------------------------------------------------------------------------- 1136 // Check that indirect function table segments are within bounds. 1137 //-------------------------------------------------------------------------- 1138 for (const WasmTableInit& table_init : module_->table_inits) { 1139 DCHECK(table_init.table_index < table_instances_.size()); 1140 uint32_t base = EvalUint32InitExpr(table_init.offset); 1141 size_t table_size = table_instances_[table_init.table_index].table_size; 1142 if (!in_bounds(base, table_init.entries.size(), table_size)) { 1143 thrower_->LinkError("table initializer is out of bounds"); 1144 return {}; 1145 } 1146 } 1147 1148 //-------------------------------------------------------------------------- 1149 // Check that memory segments are within bounds. 1150 //-------------------------------------------------------------------------- 1151 for (const WasmDataSegment& seg : module_->data_segments) { 1152 uint32_t base = EvalUint32InitExpr(seg.dest_addr); 1153 if (!in_bounds(base, seg.source.length(), instance->memory_size())) { 1154 thrower_->LinkError("data segment is out of bounds"); 1155 return {}; 1156 } 1157 } 1158 1159 //-------------------------------------------------------------------------- 1160 // Set up the exports object for the new instance. 1161 //-------------------------------------------------------------------------- 1162 ProcessExports(instance); 1163 if (thrower_->error()) return {}; 1164 1165 //-------------------------------------------------------------------------- 1166 // Initialize the indirect function tables. 1167 //-------------------------------------------------------------------------- 1168 if (table_count > 0) { 1169 LoadTableSegments(instance); 1170 } 1171 1172 //-------------------------------------------------------------------------- 1173 // Initialize the memory by loading data segments. 1174 //-------------------------------------------------------------------------- 1175 if (module_->data_segments.size() > 0) { 1176 LoadDataSegments(instance); 1177 } 1178 1179 //-------------------------------------------------------------------------- 1180 // Install a finalizer on the new instance object. 1181 //-------------------------------------------------------------------------- 1182 WasmInstanceObject::InstallFinalizer(isolate_, instance); 1183 1184 //-------------------------------------------------------------------------- 1185 // Debugging support. 1186 //-------------------------------------------------------------------------- 1187 // Set all breakpoints that were set on the shared module. 1188 WasmModuleObject::SetBreakpointsOnNewInstance(module_object_, instance); 1189 1190 if (FLAG_wasm_interpret_all && module_->origin == kWasmOrigin) { 1191 Handle<WasmDebugInfo> debug_info = 1192 WasmInstanceObject::GetOrCreateDebugInfo(instance); 1193 std::vector<int> func_indexes; 1194 for (int func_index = num_imported_functions, 1195 num_wasm_functions = static_cast<int>(module_->functions.size()); 1196 func_index < num_wasm_functions; ++func_index) { 1197 func_indexes.push_back(func_index); 1198 } 1199 WasmDebugInfo::RedirectToInterpreter( 1200 debug_info, Vector<int>(func_indexes.data(), 1201 static_cast<int>(func_indexes.size()))); 1202 } 1203 1204 //-------------------------------------------------------------------------- 1205 // Create a wrapper for the start function. 1206 //-------------------------------------------------------------------------- 1207 if (module_->start_function_index >= 0) { 1208 int start_index = module_->start_function_index; 1209 FunctionSig* sig = module_->functions[start_index].sig; 1210 Handle<Code> wrapper_code = js_to_wasm_cache_.GetOrCompileJSToWasmWrapper( 1211 isolate_, native_module, start_index, use_trap_handler()); 1212 // TODO(clemensh): Don't generate an exported function for the start 1213 // function. Use CWasmEntry instead. 1214 start_function_ = WasmExportedFunction::New( 1215 isolate_, instance, MaybeHandle<String>(), start_index, 1216 static_cast<int>(sig->parameter_count()), wrapper_code); 1217 } 1218 1219 DCHECK(!isolate_->has_pending_exception()); 1220 TRACE("Successfully built instance for module %p\n", 1221 module_object_->native_module()); 1222 return instance; 1223 } 1224 1225 bool InstanceBuilder::ExecuteStartFunction() { 1226 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.wasm"), 1227 "InstanceBuilder::ExecuteStartFunction"); 1228 if (start_function_.is_null()) return true; // No start function. 1229 1230 HandleScope scope(isolate_); 1231 // Call the JS function. 1232 Handle<Object> undefined = isolate_->factory()->undefined_value(); 1233 MaybeHandle<Object> retval = 1234 Execution::Call(isolate_, start_function_, undefined, 0, nullptr); 1235 1236 if (retval.is_null()) { 1237 DCHECK(isolate_->has_pending_exception()); 1238 return false; 1239 } 1240 return true; 1241 } 1242 1243 // Look up an import value in the {ffi_} object. 1244 MaybeHandle<Object> InstanceBuilder::LookupImport(uint32_t index, 1245 Handle<String> module_name, 1246 1247 Handle<String> import_name) { 1248 // We pre-validated in the js-api layer that the ffi object is present, and 1249 // a JSObject, if the module has imports. 1250 DCHECK(!ffi_.is_null()); 1251 1252 // Look up the module first. 1253 MaybeHandle<Object> result = Object::GetPropertyOrElement( 1254 isolate_, ffi_.ToHandleChecked(), module_name); 1255 if (result.is_null()) { 1256 return ReportTypeError("module not found", index, module_name); 1257 } 1258 1259 Handle<Object> module = result.ToHandleChecked(); 1260 1261 // Look up the value in the module. 1262 if (!module->IsJSReceiver()) { 1263 return ReportTypeError("module is not an object or function", index, 1264 module_name); 1265 } 1266 1267 result = Object::GetPropertyOrElement(isolate_, module, import_name); 1268 if (result.is_null()) { 1269 ReportLinkError("import not found", index, module_name, import_name); 1270 return MaybeHandle<JSFunction>(); 1271 } 1272 1273 return result; 1274 } 1275 1276 // Look up an import value in the {ffi_} object specifically for linking an 1277 // asm.js module. This only performs non-observable lookups, which allows 1278 // falling back to JavaScript proper (and hence re-executing all lookups) if 1279 // module instantiation fails. 1280 MaybeHandle<Object> InstanceBuilder::LookupImportAsm( 1281 uint32_t index, Handle<String> import_name) { 1282 // Check that a foreign function interface object was provided. 1283 if (ffi_.is_null()) { 1284 return ReportLinkError("missing imports object", index, import_name); 1285 } 1286 1287 // Perform lookup of the given {import_name} without causing any observable 1288 // side-effect. We only accept accesses that resolve to data properties, 1289 // which is indicated by the asm.js spec in section 7 ("Linking") as well. 1290 Handle<Object> result; 1291 LookupIterator it = LookupIterator::PropertyOrElement( 1292 isolate_, ffi_.ToHandleChecked(), import_name); 1293 switch (it.state()) { 1294 case LookupIterator::ACCESS_CHECK: 1295 case LookupIterator::INTEGER_INDEXED_EXOTIC: 1296 case LookupIterator::INTERCEPTOR: 1297 case LookupIterator::JSPROXY: 1298 case LookupIterator::ACCESSOR: 1299 case LookupIterator::TRANSITION: 1300 return ReportLinkError("not a data property", index, import_name); 1301 case LookupIterator::NOT_FOUND: 1302 // Accepting missing properties as undefined does not cause any 1303 // observable difference from JavaScript semantics, we are lenient. 1304 result = isolate_->factory()->undefined_value(); 1305 break; 1306 case LookupIterator::DATA: 1307 result = it.GetDataValue(); 1308 break; 1309 } 1310 1311 return result; 1312 } 1313 1314 uint32_t InstanceBuilder::EvalUint32InitExpr(const WasmInitExpr& expr) { 1315 switch (expr.kind) { 1316 case WasmInitExpr::kI32Const: 1317 return expr.val.i32_const; 1318 case WasmInitExpr::kGlobalIndex: { 1319 uint32_t offset = module_->globals[expr.val.global_index].offset; 1320 return ReadLittleEndianValue<uint32_t>( 1321 reinterpret_cast<Address>(raw_buffer_ptr(globals_, offset))); 1322 } 1323 default: 1324 UNREACHABLE(); 1325 } 1326 } 1327 1328 // Load data segments into the memory. 1329 void InstanceBuilder::LoadDataSegments(Handle<WasmInstanceObject> instance) { 1330 Vector<const uint8_t> wire_bytes = 1331 module_object_->native_module()->wire_bytes(); 1332 for (const WasmDataSegment& segment : module_->data_segments) { 1333 uint32_t source_size = segment.source.length(); 1334 // Segments of size == 0 are just nops. 1335 if (source_size == 0) continue; 1336 uint32_t dest_offset = EvalUint32InitExpr(segment.dest_addr); 1337 DCHECK(in_bounds(dest_offset, source_size, instance->memory_size())); 1338 byte* dest = instance->memory_start() + dest_offset; 1339 const byte* src = wire_bytes.start() + segment.source.offset(); 1340 memcpy(dest, src, source_size); 1341 } 1342 } 1343 1344 void InstanceBuilder::WriteGlobalValue(const WasmGlobal& global, double num) { 1345 TRACE("init [globals_start=%p + %u] = %lf, type = %s\n", 1346 reinterpret_cast<void*>(raw_buffer_ptr(globals_, 0)), global.offset, 1347 num, ValueTypes::TypeName(global.type)); 1348 switch (global.type) { 1349 case kWasmI32: 1350 WriteLittleEndianValue<int32_t>(GetRawGlobalPtr<int32_t>(global), 1351 static_cast<int32_t>(num)); 1352 break; 1353 case kWasmI64: 1354 // TODO(titzer): initialization of imported i64 globals. 1355 UNREACHABLE(); 1356 break; 1357 case kWasmF32: 1358 WriteLittleEndianValue<float>(GetRawGlobalPtr<float>(global), 1359 static_cast<float>(num)); 1360 break; 1361 case kWasmF64: 1362 WriteLittleEndianValue<double>(GetRawGlobalPtr<double>(global), 1363 static_cast<double>(num)); 1364 break; 1365 default: 1366 UNREACHABLE(); 1367 } 1368 } 1369 1370 void InstanceBuilder::WriteGlobalValue(const WasmGlobal& global, 1371 Handle<WasmGlobalObject> value) { 1372 TRACE("init [globals_start=%p + %u] = ", 1373 reinterpret_cast<void*>(raw_buffer_ptr(globals_, 0)), global.offset); 1374 switch (global.type) { 1375 case kWasmI32: { 1376 int32_t num = value->GetI32(); 1377 WriteLittleEndianValue<int32_t>(GetRawGlobalPtr<int32_t>(global), num); 1378 TRACE("%d", num); 1379 break; 1380 } 1381 case kWasmI64: { 1382 int64_t num = value->GetI64(); 1383 WriteLittleEndianValue<int64_t>(GetRawGlobalPtr<int64_t>(global), num); 1384 TRACE("%" PRId64, num); 1385 break; 1386 } 1387 case kWasmF32: { 1388 float num = value->GetF32(); 1389 WriteLittleEndianValue<float>(GetRawGlobalPtr<float>(global), num); 1390 TRACE("%f", num); 1391 break; 1392 } 1393 case kWasmF64: { 1394 double num = value->GetF64(); 1395 WriteLittleEndianValue<double>(GetRawGlobalPtr<double>(global), num); 1396 TRACE("%lf", num); 1397 break; 1398 } 1399 default: 1400 UNREACHABLE(); 1401 } 1402 TRACE(", type = %s (from WebAssembly.Global)\n", 1403 ValueTypes::TypeName(global.type)); 1404 } 1405 1406 void InstanceBuilder::SanitizeImports() { 1407 Vector<const uint8_t> wire_bytes = 1408 module_object_->native_module()->wire_bytes(); 1409 for (size_t index = 0; index < module_->import_table.size(); ++index) { 1410 const WasmImport& import = module_->import_table[index]; 1411 1412 Handle<String> module_name; 1413 MaybeHandle<String> maybe_module_name = 1414 WasmModuleObject::ExtractUtf8StringFromModuleBytes(isolate_, wire_bytes, 1415 import.module_name); 1416 if (!maybe_module_name.ToHandle(&module_name)) { 1417 thrower_->LinkError("Could not resolve module name for import %zu", 1418 index); 1419 return; 1420 } 1421 1422 Handle<String> import_name; 1423 MaybeHandle<String> maybe_import_name = 1424 WasmModuleObject::ExtractUtf8StringFromModuleBytes(isolate_, wire_bytes, 1425 import.field_name); 1426 if (!maybe_import_name.ToHandle(&import_name)) { 1427 thrower_->LinkError("Could not resolve import name for import %zu", 1428 index); 1429 return; 1430 } 1431 1432 int int_index = static_cast<int>(index); 1433 MaybeHandle<Object> result = 1434 module_->origin == kAsmJsOrigin 1435 ? LookupImportAsm(int_index, import_name) 1436 : LookupImport(int_index, module_name, import_name); 1437 if (thrower_->error()) { 1438 thrower_->LinkError("Could not find value for import %zu", index); 1439 return; 1440 } 1441 Handle<Object> value = result.ToHandleChecked(); 1442 sanitized_imports_.push_back({module_name, import_name, value}); 1443 } 1444 } 1445 1446 MaybeHandle<JSArrayBuffer> InstanceBuilder::FindImportedMemoryBuffer() const { 1447 DCHECK_EQ(module_->import_table.size(), sanitized_imports_.size()); 1448 for (size_t index = 0; index < module_->import_table.size(); index++) { 1449 const WasmImport& import = module_->import_table[index]; 1450 1451 if (import.kind == kExternalMemory) { 1452 const auto& value = sanitized_imports_[index].value; 1453 if (!value->IsWasmMemoryObject()) { 1454 return {}; 1455 } 1456 auto memory = Handle<WasmMemoryObject>::cast(value); 1457 Handle<JSArrayBuffer> buffer(memory->array_buffer(), isolate_); 1458 return buffer; 1459 } 1460 } 1461 return {}; 1462 } 1463 1464 // Process the imports, including functions, tables, globals, and memory, in 1465 // order, loading them from the {ffi_} object. Returns the number of imported 1466 // functions. 1467 int InstanceBuilder::ProcessImports(Handle<WasmInstanceObject> instance) { 1468 int num_imported_functions = 0; 1469 int num_imported_tables = 0; 1470 int num_imported_mutable_globals = 0; 1471 1472 DCHECK_EQ(module_->import_table.size(), sanitized_imports_.size()); 1473 int num_imports = static_cast<int>(module_->import_table.size()); 1474 NativeModule* native_module = instance->module_object()->native_module(); 1475 for (int index = 0; index < num_imports; ++index) { 1476 const WasmImport& import = module_->import_table[index]; 1477 1478 Handle<String> module_name = sanitized_imports_[index].module_name; 1479 Handle<String> import_name = sanitized_imports_[index].import_name; 1480 Handle<Object> value = sanitized_imports_[index].value; 1481 1482 switch (import.kind) { 1483 case kExternalFunction: { 1484 // Function imports must be callable. 1485 if (!value->IsCallable()) { 1486 ReportLinkError("function import requires a callable", index, 1487 module_name, import_name); 1488 return -1; 1489 } 1490 uint32_t func_index = import.index; 1491 DCHECK_EQ(num_imported_functions, func_index); 1492 FunctionSig* expected_sig = module_->functions[func_index].sig; 1493 if (WasmExportedFunction::IsWasmExportedFunction(*value)) { 1494 // The imported function is a WASM function from another instance. 1495 Handle<WasmExportedFunction> imported_function( 1496 WasmExportedFunction::cast(*value), isolate_); 1497 Handle<WasmInstanceObject> imported_instance( 1498 imported_function->instance(), isolate_); 1499 FunctionSig* imported_sig = 1500 imported_instance->module() 1501 ->functions[imported_function->function_index()] 1502 .sig; 1503 if (*imported_sig != *expected_sig) { 1504 ReportLinkError( 1505 "imported function does not match the expected type", index, 1506 module_name, import_name); 1507 return -1; 1508 } 1509 // The import reference is the instance object itself. 1510 Address imported_target = imported_function->GetWasmCallTarget(); 1511 ImportedFunctionEntry entry(instance, func_index); 1512 entry.set_wasm_to_wasm(*imported_instance, imported_target); 1513 } else { 1514 // The imported function is a callable. 1515 Handle<JSReceiver> js_receiver(JSReceiver::cast(*value), isolate_); 1516 Handle<Code> wrapper_code = 1517 compiler::CompileWasmToJSWrapper( 1518 isolate_, js_receiver, expected_sig, func_index, 1519 module_->origin, use_trap_handler()) 1520 .ToHandleChecked(); 1521 RecordStats(*wrapper_code, isolate_->counters()); 1522 1523 WasmCode* wasm_code = native_module->AddCodeCopy( 1524 wrapper_code, WasmCode::kWasmToJsWrapper, func_index); 1525 ImportedFunctionEntry entry(instance, func_index); 1526 entry.set_wasm_to_js(*js_receiver, wasm_code); 1527 } 1528 num_imported_functions++; 1529 break; 1530 } 1531 case kExternalTable: { 1532 if (!value->IsWasmTableObject()) { 1533 ReportLinkError("table import requires a WebAssembly.Table", index, 1534 module_name, import_name); 1535 return -1; 1536 } 1537 uint32_t table_num = import.index; 1538 DCHECK_EQ(table_num, num_imported_tables); 1539 const WasmTable& table = module_->tables[table_num]; 1540 TableInstance& table_instance = table_instances_[table_num]; 1541 table_instance.table_object = Handle<WasmTableObject>::cast(value); 1542 instance->set_table_object(*table_instance.table_object); 1543 table_instance.js_wrappers = Handle<FixedArray>( 1544 table_instance.table_object->functions(), isolate_); 1545 1546 int imported_table_size = table_instance.js_wrappers->length(); 1547 if (imported_table_size < static_cast<int>(table.initial_size)) { 1548 thrower_->LinkError( 1549 "table import %d is smaller than initial %d, got %u", index, 1550 table.initial_size, imported_table_size); 1551 return -1; 1552 } 1553 1554 if (table.has_maximum_size) { 1555 int64_t imported_maximum_size = 1556 table_instance.table_object->maximum_length()->Number(); 1557 if (imported_maximum_size < 0) { 1558 thrower_->LinkError( 1559 "table import %d has no maximum length, expected %d", index, 1560 table.maximum_size); 1561 return -1; 1562 } 1563 if (imported_maximum_size > table.maximum_size) { 1564 thrower_->LinkError( 1565 " table import %d has a larger maximum size %" PRIx64 1566 " than the module's declared maximum %u", 1567 index, imported_maximum_size, table.maximum_size); 1568 return -1; 1569 } 1570 } 1571 1572 // Allocate a new dispatch table. 1573 if (!instance->has_indirect_function_table()) { 1574 WasmInstanceObject::EnsureIndirectFunctionTableWithMinimumSize( 1575 instance, imported_table_size); 1576 table_instances_[table_num].table_size = imported_table_size; 1577 } 1578 // Initialize the dispatch table with the (foreign) JS functions 1579 // that are already in the table. 1580 for (int i = 0; i < imported_table_size; ++i) { 1581 Handle<Object> val(table_instance.js_wrappers->get(i), isolate_); 1582 // TODO(mtrofin): this is the same logic as WasmTableObject::Set: 1583 // insert in the local table a wrapper from the other module, and add 1584 // a reference to the owning instance of the other module. 1585 if (!val->IsJSFunction()) continue; 1586 if (!WasmExportedFunction::IsWasmExportedFunction(*val)) { 1587 thrower_->LinkError("table import %d[%d] is not a wasm function", 1588 index, i); 1589 return -1; 1590 } 1591 // Look up the signature's canonical id. If there is no canonical 1592 // id, then the signature does not appear at all in this module, 1593 // so putting {-1} in the table will cause checks to always fail. 1594 auto target = Handle<WasmExportedFunction>::cast(val); 1595 Handle<WasmInstanceObject> imported_instance = 1596 handle(target->instance(), isolate_); 1597 Address exported_call_target = target->GetWasmCallTarget(); 1598 FunctionSig* sig = imported_instance->module() 1599 ->functions[target->function_index()] 1600 .sig; 1601 IndirectFunctionTableEntry(instance, i) 1602 .set(module_->signature_map.Find(*sig), *imported_instance, 1603 exported_call_target); 1604 } 1605 num_imported_tables++; 1606 break; 1607 } 1608 case kExternalMemory: { 1609 // Validation should have failed if more than one memory object was 1610 // provided. 1611 DCHECK(!instance->has_memory_object()); 1612 if (!value->IsWasmMemoryObject()) { 1613 ReportLinkError("memory import must be a WebAssembly.Memory object", 1614 index, module_name, import_name); 1615 return -1; 1616 } 1617 auto memory = Handle<WasmMemoryObject>::cast(value); 1618 instance->set_memory_object(*memory); 1619 Handle<JSArrayBuffer> buffer(memory->array_buffer(), isolate_); 1620 // memory_ should have already been assigned in Build(). 1621 DCHECK_EQ(*memory_.ToHandleChecked(), *buffer); 1622 uint32_t imported_cur_pages = static_cast<uint32_t>( 1623 buffer->byte_length()->Number() / kWasmPageSize); 1624 if (imported_cur_pages < module_->initial_pages) { 1625 thrower_->LinkError( 1626 "memory import %d is smaller than initial %u, got %u", index, 1627 module_->initial_pages, imported_cur_pages); 1628 } 1629 int32_t imported_maximum_pages = memory->maximum_pages(); 1630 if (module_->has_maximum_pages) { 1631 if (imported_maximum_pages < 0) { 1632 thrower_->LinkError( 1633 "memory import %d has no maximum limit, expected at most %u", 1634 index, imported_maximum_pages); 1635 return -1; 1636 } 1637 if (static_cast<uint32_t>(imported_maximum_pages) > 1638 module_->maximum_pages) { 1639 thrower_->LinkError( 1640 "memory import %d has a larger maximum size %u than the " 1641 "module's declared maximum %u", 1642 index, imported_maximum_pages, module_->maximum_pages); 1643 return -1; 1644 } 1645 } 1646 if (module_->has_shared_memory != buffer->is_shared()) { 1647 thrower_->LinkError( 1648 "mismatch in shared state of memory, declared = %d, imported = " 1649 "%d", 1650 module_->has_shared_memory, buffer->is_shared()); 1651 return -1; 1652 } 1653 1654 break; 1655 } 1656 case kExternalGlobal: { 1657 // Immutable global imports are converted to numbers and written into 1658 // the {globals_} array buffer. 1659 // 1660 // Mutable global imports instead have their backing array buffers 1661 // referenced by this instance, and store the address of the imported 1662 // global in the {imported_mutable_globals_} array. 1663 const WasmGlobal& global = module_->globals[import.index]; 1664 1665 // The mutable-global proposal allows importing i64 values, but only if 1666 // they are passed as a WebAssembly.Global object. 1667 if (global.type == kWasmI64 && 1668 !(enabled_.mut_global && value->IsWasmGlobalObject())) { 1669 ReportLinkError("global import cannot have type i64", index, 1670 module_name, import_name); 1671 return -1; 1672 } 1673 if (module_->origin == kAsmJsOrigin) { 1674 // Accepting {JSFunction} on top of just primitive values here is a 1675 // workaround to support legacy asm.js code with broken binding. Note 1676 // that using {NaN} (or Smi::kZero) here is what using the observable 1677 // conversion via {ToPrimitive} would produce as well. 1678 // TODO(mstarzinger): Still observable if Function.prototype.valueOf 1679 // or friends are patched, we might need to check for that as well. 1680 if (value->IsJSFunction()) value = isolate_->factory()->nan_value(); 1681 if (value->IsPrimitive() && !value->IsSymbol()) { 1682 if (global.type == kWasmI32) { 1683 value = Object::ToInt32(isolate_, value).ToHandleChecked(); 1684 } else { 1685 value = Object::ToNumber(isolate_, value).ToHandleChecked(); 1686 } 1687 } 1688 } 1689 if (enabled_.mut_global) { 1690 if (value->IsWasmGlobalObject()) { 1691 auto global_object = Handle<WasmGlobalObject>::cast(value); 1692 if (global_object->type() != global.type) { 1693 ReportLinkError( 1694 "imported global does not match the expected type", index, 1695 module_name, import_name); 1696 return -1; 1697 } 1698 if (global_object->is_mutable() != global.mutability) { 1699 ReportLinkError( 1700 "imported global does not match the expected mutability", 1701 index, module_name, import_name); 1702 return -1; 1703 } 1704 if (global.mutability) { 1705 Handle<JSArrayBuffer> buffer(global_object->array_buffer(), 1706 isolate_); 1707 int index = num_imported_mutable_globals++; 1708 instance->imported_mutable_globals_buffers()->set(index, *buffer); 1709 // It is safe in this case to store the raw pointer to the buffer 1710 // since the backing store of the JSArrayBuffer will not be 1711 // relocated. 1712 instance->imported_mutable_globals()[index] = 1713 reinterpret_cast<Address>( 1714 raw_buffer_ptr(buffer, global_object->offset())); 1715 } else { 1716 WriteGlobalValue(global, global_object); 1717 } 1718 } else if (value->IsNumber()) { 1719 if (global.mutability) { 1720 ReportLinkError( 1721 "imported mutable global must be a WebAssembly.Global object", 1722 index, module_name, import_name); 1723 return -1; 1724 } 1725 WriteGlobalValue(global, value->Number()); 1726 } else { 1727 ReportLinkError( 1728 "global import must be a number or WebAssembly.Global object", 1729 index, module_name, import_name); 1730 return -1; 1731 } 1732 } else { 1733 if (value->IsNumber()) { 1734 WriteGlobalValue(global, value->Number()); 1735 } else { 1736 ReportLinkError("global import must be a number", index, 1737 module_name, import_name); 1738 return -1; 1739 } 1740 } 1741 break; 1742 } 1743 default: 1744 UNREACHABLE(); 1745 break; 1746 } 1747 } 1748 1749 DCHECK_EQ(module_->num_imported_mutable_globals, 1750 num_imported_mutable_globals); 1751 1752 return num_imported_functions; 1753 } 1754 1755 template <typename T> 1756 T* InstanceBuilder::GetRawGlobalPtr(const WasmGlobal& global) { 1757 return reinterpret_cast<T*>(raw_buffer_ptr(globals_, global.offset)); 1758 } 1759 1760 // Process initialization of globals. 1761 void InstanceBuilder::InitGlobals() { 1762 for (auto global : module_->globals) { 1763 if (global.mutability && global.imported) { 1764 continue; 1765 } 1766 1767 switch (global.init.kind) { 1768 case WasmInitExpr::kI32Const: 1769 WriteLittleEndianValue<int32_t>(GetRawGlobalPtr<int32_t>(global), 1770 global.init.val.i32_const); 1771 break; 1772 case WasmInitExpr::kI64Const: 1773 WriteLittleEndianValue<int64_t>(GetRawGlobalPtr<int64_t>(global), 1774 global.init.val.i64_const); 1775 break; 1776 case WasmInitExpr::kF32Const: 1777 WriteLittleEndianValue<float>(GetRawGlobalPtr<float>(global), 1778 global.init.val.f32_const); 1779 break; 1780 case WasmInitExpr::kF64Const: 1781 WriteLittleEndianValue<double>(GetRawGlobalPtr<double>(global), 1782 global.init.val.f64_const); 1783 break; 1784 case WasmInitExpr::kGlobalIndex: { 1785 // Initialize with another global. 1786 uint32_t new_offset = global.offset; 1787 uint32_t old_offset = 1788 module_->globals[global.init.val.global_index].offset; 1789 TRACE("init [globals+%u] = [globals+%d]\n", global.offset, old_offset); 1790 size_t size = (global.type == kWasmI64 || global.type == kWasmF64) 1791 ? sizeof(double) 1792 : sizeof(int32_t); 1793 memcpy(raw_buffer_ptr(globals_, new_offset), 1794 raw_buffer_ptr(globals_, old_offset), size); 1795 break; 1796 } 1797 case WasmInitExpr::kNone: 1798 // Happens with imported globals. 1799 break; 1800 default: 1801 UNREACHABLE(); 1802 break; 1803 } 1804 } 1805 } 1806 1807 // Allocate memory for a module instance as a new JSArrayBuffer. 1808 Handle<JSArrayBuffer> InstanceBuilder::AllocateMemory(uint32_t num_pages) { 1809 if (num_pages > FLAG_wasm_max_mem_pages) { 1810 thrower_->RangeError("Out of memory: wasm memory too large"); 1811 return Handle<JSArrayBuffer>::null(); 1812 } 1813 const bool is_shared_memory = module_->has_shared_memory && enabled_.threads; 1814 i::SharedFlag shared_flag = 1815 is_shared_memory ? i::SharedFlag::kShared : i::SharedFlag::kNotShared; 1816 Handle<JSArrayBuffer> mem_buffer; 1817 if (!NewArrayBuffer(isolate_, num_pages * kWasmPageSize, shared_flag) 1818 .ToHandle(&mem_buffer)) { 1819 thrower_->RangeError("Out of memory: wasm memory"); 1820 } 1821 return mem_buffer; 1822 } 1823 1824 bool InstanceBuilder::NeedsWrappers() const { 1825 if (module_->num_exported_functions > 0) return true; 1826 for (auto& table_instance : table_instances_) { 1827 if (!table_instance.js_wrappers.is_null()) return true; 1828 } 1829 for (auto& table : module_->tables) { 1830 if (table.exported) return true; 1831 } 1832 return false; 1833 } 1834 1835 // Process the exports, creating wrappers for functions, tables, memories, 1836 // and globals. 1837 void InstanceBuilder::ProcessExports(Handle<WasmInstanceObject> instance) { 1838 Handle<FixedArray> export_wrappers(module_object_->export_wrappers(), 1839 isolate_); 1840 if (NeedsWrappers()) { 1841 // Fill the table to cache the exported JSFunction wrappers. 1842 js_wrappers_.insert(js_wrappers_.begin(), module_->functions.size(), 1843 Handle<JSFunction>::null()); 1844 1845 // If an imported WebAssembly function gets exported, the exported function 1846 // has to be identical to to imported function. Therefore we put all 1847 // imported WebAssembly functions into the js_wrappers_ list. 1848 for (int index = 0, end = static_cast<int>(module_->import_table.size()); 1849 index < end; ++index) { 1850 const WasmImport& import = module_->import_table[index]; 1851 if (import.kind == kExternalFunction) { 1852 Handle<Object> value = sanitized_imports_[index].value; 1853 if (WasmExportedFunction::IsWasmExportedFunction(*value)) { 1854 js_wrappers_[import.index] = Handle<JSFunction>::cast(value); 1855 } 1856 } 1857 } 1858 } 1859 1860 Handle<JSObject> exports_object; 1861 bool is_asm_js = false; 1862 switch (module_->origin) { 1863 case kWasmOrigin: { 1864 // Create the "exports" object. 1865 exports_object = isolate_->factory()->NewJSObjectWithNullProto(); 1866 break; 1867 } 1868 case kAsmJsOrigin: { 1869 Handle<JSFunction> object_function = Handle<JSFunction>( 1870 isolate_->native_context()->object_function(), isolate_); 1871 exports_object = isolate_->factory()->NewJSObject(object_function); 1872 is_asm_js = true; 1873 break; 1874 } 1875 default: 1876 UNREACHABLE(); 1877 } 1878 instance->set_exports_object(*exports_object); 1879 1880 Handle<String> single_function_name = 1881 isolate_->factory()->InternalizeUtf8String(AsmJs::kSingleFunctionName); 1882 1883 PropertyDescriptor desc; 1884 desc.set_writable(is_asm_js); 1885 desc.set_enumerable(true); 1886 desc.set_configurable(is_asm_js); 1887 1888 // Process each export in the export table. 1889 int export_index = 0; // Index into {export_wrappers}. 1890 for (const WasmExport& exp : module_->export_table) { 1891 Handle<String> name = WasmModuleObject::ExtractUtf8StringFromModuleBytes( 1892 isolate_, module_object_, exp.name) 1893 .ToHandleChecked(); 1894 Handle<JSObject> export_to; 1895 if (is_asm_js && exp.kind == kExternalFunction && 1896 String::Equals(isolate_, name, single_function_name)) { 1897 export_to = instance; 1898 } else { 1899 export_to = exports_object; 1900 } 1901 1902 switch (exp.kind) { 1903 case kExternalFunction: { 1904 // Wrap and export the code as a JSFunction. 1905 const WasmFunction& function = module_->functions[exp.index]; 1906 Handle<JSFunction> js_function = js_wrappers_[exp.index]; 1907 if (js_function.is_null()) { 1908 // Wrap the exported code as a JSFunction. 1909 Handle<Code> export_code = 1910 export_wrappers->GetValueChecked<Code>(isolate_, export_index); 1911 MaybeHandle<String> func_name; 1912 if (is_asm_js) { 1913 // For modules arising from asm.js, honor the names section. 1914 WireBytesRef func_name_ref = module_->LookupFunctionName( 1915 module_object_->native_module()->wire_bytes(), 1916 function.func_index); 1917 func_name = WasmModuleObject::ExtractUtf8StringFromModuleBytes( 1918 isolate_, module_object_, func_name_ref) 1919 .ToHandleChecked(); 1920 } 1921 js_function = WasmExportedFunction::New( 1922 isolate_, instance, func_name, function.func_index, 1923 static_cast<int>(function.sig->parameter_count()), export_code); 1924 js_wrappers_[exp.index] = js_function; 1925 } 1926 desc.set_value(js_function); 1927 export_index++; 1928 break; 1929 } 1930 case kExternalTable: { 1931 // Export a table as a WebAssembly.Table object. 1932 TableInstance& table_instance = table_instances_[exp.index]; 1933 const WasmTable& table = module_->tables[exp.index]; 1934 if (table_instance.table_object.is_null()) { 1935 uint32_t maximum = table.has_maximum_size ? table.maximum_size 1936 : FLAG_wasm_max_table_size; 1937 table_instance.table_object = 1938 WasmTableObject::New(isolate_, table.initial_size, maximum, 1939 &table_instance.js_wrappers); 1940 } 1941 desc.set_value(table_instance.table_object); 1942 break; 1943 } 1944 case kExternalMemory: { 1945 // Export the memory as a WebAssembly.Memory object. A WasmMemoryObject 1946 // should already be available if the module has memory, since we always 1947 // create or import it when building an WasmInstanceObject. 1948 DCHECK(instance->has_memory_object()); 1949 desc.set_value( 1950 Handle<WasmMemoryObject>(instance->memory_object(), isolate_)); 1951 break; 1952 } 1953 case kExternalGlobal: { 1954 const WasmGlobal& global = module_->globals[exp.index]; 1955 if (enabled_.mut_global) { 1956 Handle<JSArrayBuffer> buffer; 1957 uint32_t offset; 1958 1959 if (global.mutability && global.imported) { 1960 Handle<FixedArray> buffers_array( 1961 instance->imported_mutable_globals_buffers(), isolate_); 1962 buffer = buffers_array->GetValueChecked<JSArrayBuffer>( 1963 isolate_, global.index); 1964 Address global_addr = 1965 instance->imported_mutable_globals()[global.index]; 1966 1967 uint32_t buffer_size = 0; 1968 CHECK(buffer->byte_length()->ToUint32(&buffer_size)); 1969 1970 Address backing_store = 1971 reinterpret_cast<Address>(buffer->backing_store()); 1972 CHECK(global_addr >= backing_store && 1973 global_addr < backing_store + buffer_size); 1974 offset = static_cast<uint32_t>(global_addr - backing_store); 1975 } else { 1976 buffer = handle(instance->globals_buffer(), isolate_); 1977 offset = global.offset; 1978 } 1979 1980 // Since the global's array buffer is always provided, allocation 1981 // should never fail. 1982 Handle<WasmGlobalObject> global_obj = 1983 WasmGlobalObject::New(isolate_, buffer, global.type, offset, 1984 global.mutability) 1985 .ToHandleChecked(); 1986 desc.set_value(global_obj); 1987 } else { 1988 // Export the value of the global variable as a number. 1989 double num = 0; 1990 switch (global.type) { 1991 case kWasmI32: 1992 num = ReadLittleEndianValue<int32_t>( 1993 GetRawGlobalPtr<int32_t>(global)); 1994 break; 1995 case kWasmF32: 1996 num = 1997 ReadLittleEndianValue<float>(GetRawGlobalPtr<float>(global)); 1998 break; 1999 case kWasmF64: 2000 num = ReadLittleEndianValue<double>( 2001 GetRawGlobalPtr<double>(global)); 2002 break; 2003 case kWasmI64: 2004 thrower_->LinkError( 2005 "export of globals of type I64 is not allowed."); 2006 return; 2007 default: 2008 UNREACHABLE(); 2009 } 2010 desc.set_value(isolate_->factory()->NewNumber(num)); 2011 } 2012 break; 2013 } 2014 default: 2015 UNREACHABLE(); 2016 break; 2017 } 2018 2019 v8::Maybe<bool> status = JSReceiver::DefineOwnProperty( 2020 isolate_, export_to, name, &desc, kThrowOnError); 2021 if (!status.IsJust()) { 2022 TruncatedUserString<> trunc_name(name->GetCharVector<uint8_t>()); 2023 thrower_->LinkError("export of %.*s failed.", trunc_name.length(), 2024 trunc_name.start()); 2025 return; 2026 } 2027 } 2028 DCHECK_EQ(export_index, export_wrappers->length()); 2029 2030 if (module_->origin == kWasmOrigin) { 2031 v8::Maybe<bool> success = 2032 JSReceiver::SetIntegrityLevel(exports_object, FROZEN, kDontThrow); 2033 DCHECK(success.FromMaybe(false)); 2034 USE(success); 2035 } 2036 } 2037 2038 void InstanceBuilder::InitializeTables(Handle<WasmInstanceObject> instance) { 2039 size_t table_count = module_->tables.size(); 2040 for (size_t index = 0; index < table_count; ++index) { 2041 const WasmTable& table = module_->tables[index]; 2042 TableInstance& table_instance = table_instances_[index]; 2043 2044 if (!instance->has_indirect_function_table() && 2045 table.type == kWasmAnyFunc) { 2046 WasmInstanceObject::EnsureIndirectFunctionTableWithMinimumSize( 2047 instance, table.initial_size); 2048 table_instance.table_size = table.initial_size; 2049 } 2050 } 2051 } 2052 2053 void InstanceBuilder::LoadTableSegments(Handle<WasmInstanceObject> instance) { 2054 NativeModule* native_module = module_object_->native_module(); 2055 for (auto& table_init : module_->table_inits) { 2056 uint32_t base = EvalUint32InitExpr(table_init.offset); 2057 uint32_t num_entries = static_cast<uint32_t>(table_init.entries.size()); 2058 uint32_t index = table_init.table_index; 2059 TableInstance& table_instance = table_instances_[index]; 2060 DCHECK(in_bounds(base, num_entries, table_instance.table_size)); 2061 for (uint32_t i = 0; i < num_entries; ++i) { 2062 uint32_t func_index = table_init.entries[i]; 2063 const WasmFunction* function = &module_->functions[func_index]; 2064 int table_index = static_cast<int>(i + base); 2065 2066 // Update the local dispatch table first. 2067 uint32_t sig_id = module_->signature_ids[function->sig_index]; 2068 Handle<WasmInstanceObject> target_instance = instance; 2069 Address call_target; 2070 const bool is_import = func_index < module_->num_imported_functions; 2071 if (is_import) { 2072 // For imported calls, take target instance and address from the 2073 // import table. 2074 ImportedFunctionEntry entry(instance, func_index); 2075 target_instance = handle(entry.instance(), isolate_); 2076 call_target = entry.target(); 2077 } else { 2078 call_target = native_module->GetCallTargetForFunction(func_index); 2079 } 2080 IndirectFunctionTableEntry(instance, table_index) 2081 .set(sig_id, *target_instance, call_target); 2082 2083 if (!table_instance.table_object.is_null()) { 2084 // Update the table object's other dispatch tables. 2085 if (js_wrappers_[func_index].is_null()) { 2086 // No JSFunction entry yet exists for this function. Create one. 2087 // TODO(titzer): We compile JS->wasm wrappers for functions are 2088 // not exported but are in an exported table. This should be done 2089 // at module compile time and cached instead. 2090 2091 Handle<Code> wrapper_code = 2092 js_to_wasm_cache_.GetOrCompileJSToWasmWrapper( 2093 isolate_, native_module, func_index, use_trap_handler()); 2094 MaybeHandle<String> func_name; 2095 if (module_->origin == kAsmJsOrigin) { 2096 // For modules arising from asm.js, honor the names section. 2097 WireBytesRef func_name_ref = module_->LookupFunctionName( 2098 native_module->wire_bytes(), func_index); 2099 func_name = WasmModuleObject::ExtractUtf8StringFromModuleBytes( 2100 isolate_, module_object_, func_name_ref) 2101 .ToHandleChecked(); 2102 } 2103 Handle<WasmExportedFunction> js_function = WasmExportedFunction::New( 2104 isolate_, instance, func_name, func_index, 2105 static_cast<int>(function->sig->parameter_count()), wrapper_code); 2106 js_wrappers_[func_index] = js_function; 2107 } 2108 table_instance.js_wrappers->set(table_index, *js_wrappers_[func_index]); 2109 // UpdateDispatchTables() updates all other dispatch tables, since 2110 // we have not yet added the dispatch table we are currently building. 2111 WasmTableObject::UpdateDispatchTables( 2112 isolate_, table_instance.table_object, table_index, function->sig, 2113 target_instance, call_target); 2114 } 2115 } 2116 } 2117 2118 int table_count = static_cast<int>(module_->tables.size()); 2119 for (int index = 0; index < table_count; ++index) { 2120 TableInstance& table_instance = table_instances_[index]; 2121 2122 // Add the new dispatch table at the end to avoid redundant lookups. 2123 if (!table_instance.table_object.is_null()) { 2124 WasmTableObject::AddDispatchTable(isolate_, table_instance.table_object, 2125 instance, index); 2126 } 2127 } 2128 } 2129 2130 AsyncCompileJob::AsyncCompileJob( 2131 Isolate* isolate, const WasmFeatures& enabled, 2132 std::unique_ptr<byte[]> bytes_copy, size_t length, Handle<Context> context, 2133 std::shared_ptr<CompilationResultResolver> resolver) 2134 : isolate_(isolate), 2135 enabled_features_(enabled), 2136 async_counters_(isolate->async_counters()), 2137 bytes_copy_(std::move(bytes_copy)), 2138 wire_bytes_(bytes_copy_.get(), bytes_copy_.get() + length), 2139 resolver_(std::move(resolver)) { 2140 v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate); 2141 v8::Platform* platform = V8::GetCurrentPlatform(); 2142 foreground_task_runner_ = platform->GetForegroundTaskRunner(v8_isolate); 2143 // The handle for the context must be deferred. 2144 DeferredHandleScope deferred(isolate); 2145 native_context_ = Handle<Context>(context->native_context(), isolate); 2146 DCHECK(native_context_->IsNativeContext()); 2147 deferred_handles_.push_back(deferred.Detach()); 2148 } 2149 2150 void AsyncCompileJob::Start() { 2151 DoAsync<DecodeModule>(); // -- 2152 } 2153 2154 void AsyncCompileJob::Abort() { 2155 // Removing this job will trigger the destructor, which will cancel all 2156 // compilation. 2157 isolate_->wasm_engine()->RemoveCompileJob(this); 2158 } 2159 2160 class AsyncStreamingProcessor final : public StreamingProcessor { 2161 public: 2162 explicit AsyncStreamingProcessor(AsyncCompileJob* job); 2163 2164 bool ProcessModuleHeader(Vector<const uint8_t> bytes, 2165 uint32_t offset) override; 2166 2167 bool ProcessSection(SectionCode section_code, Vector<const uint8_t> bytes, 2168 uint32_t offset) override; 2169 2170 bool ProcessCodeSectionHeader(size_t functions_count, 2171 uint32_t offset) override; 2172 2173 bool ProcessFunctionBody(Vector<const uint8_t> bytes, 2174 uint32_t offset) override; 2175 2176 void OnFinishedChunk() override; 2177 2178 void OnFinishedStream(OwnedVector<uint8_t> bytes) override; 2179 2180 void OnError(DecodeResult result) override; 2181 2182 void OnAbort() override; 2183 2184 private: 2185 // Finishes the AsyncCompileJob with an error. 2186 void FinishAsyncCompileJobWithError(ResultBase result); 2187 2188 void CommitCompilationUnits(); 2189 2190 ModuleDecoder decoder_; 2191 AsyncCompileJob* job_; 2192 std::unique_ptr<CompilationUnitBuilder> compilation_unit_builder_; 2193 uint32_t next_function_ = 0; 2194 }; 2195 2196 std::shared_ptr<StreamingDecoder> AsyncCompileJob::CreateStreamingDecoder() { 2197 DCHECK_NULL(stream_); 2198 stream_.reset( 2199 new StreamingDecoder(base::make_unique<AsyncStreamingProcessor>(this))); 2200 return stream_; 2201 } 2202 2203 AsyncCompileJob::~AsyncCompileJob() { 2204 background_task_manager_.CancelAndWait(); 2205 if (native_module_) native_module_->compilation_state()->Abort(); 2206 // Tell the streaming decoder that the AsyncCompileJob is not available 2207 // anymore. 2208 // TODO(ahaas): Is this notification really necessary? Check 2209 // https://crbug.com/888170. 2210 if (stream_) stream_->NotifyCompilationEnded(); 2211 CancelPendingForegroundTask(); 2212 for (auto d : deferred_handles_) delete d; 2213 } 2214 2215 // This function assumes that it is executed in a HandleScope, and that a 2216 // context is set on the isolate. 2217 void AsyncCompileJob::FinishCompile() { 2218 DCHECK_NOT_NULL(isolate_->context()); 2219 // Finish the wasm script now and make it public to the debugger. 2220 Handle<Script> script(module_object_->script(), isolate_); 2221 isolate_->debug()->OnAfterCompile(script); 2222 2223 // Log the code within the generated module for profiling. 2224 native_module_->LogWasmCodes(isolate_); 2225 2226 // We can only update the feature counts once the entire compile is done. 2227 auto compilation_state = native_module_->compilation_state(); 2228 compilation_state->PublishDetectedFeatures( 2229 isolate_, *compilation_state->detected_features()); 2230 2231 // TODO(wasm): compiling wrappers should be made async as well. 2232 DoSync<CompileWrappers>(); 2233 } 2234 2235 void AsyncCompileJob::AsyncCompileFailed(Handle<Object> error_reason) { 2236 // {job} keeps the {this} pointer alive. 2237 std::shared_ptr<AsyncCompileJob> job = 2238 isolate_->wasm_engine()->RemoveCompileJob(this); 2239 resolver_->OnCompilationFailed(error_reason); 2240 } 2241 2242 void AsyncCompileJob::AsyncCompileSucceeded(Handle<WasmModuleObject> result) { 2243 resolver_->OnCompilationSucceeded(result); 2244 } 2245 2246 // A closure to run a compilation step (either as foreground or background 2247 // task) and schedule the next step(s), if any. 2248 class AsyncCompileJob::CompileStep { 2249 public: 2250 virtual ~CompileStep() {} 2251 2252 void Run(bool on_foreground) { 2253 if (on_foreground) { 2254 HandleScope scope(job_->isolate_); 2255 SaveContext saved_context(job_->isolate_); 2256 job_->isolate_->set_context(*job_->native_context_); 2257 RunInForeground(); 2258 } else { 2259 RunInBackground(); 2260 } 2261 } 2262 2263 virtual void RunInForeground() { UNREACHABLE(); } 2264 virtual void RunInBackground() { UNREACHABLE(); } 2265 2266 AsyncCompileJob* job_ = nullptr; 2267 }; 2268 2269 class AsyncCompileJob::CompileTask : public CancelableTask { 2270 public: 2271 CompileTask(AsyncCompileJob* job, bool on_foreground) 2272 // We only manage the background tasks with the {CancelableTaskManager} of 2273 // the {AsyncCompileJob}. Foreground tasks are managed by the system's 2274 // {CancelableTaskManager}. Background tasks cannot spawn tasks managed by 2275 // their own task manager. 2276 : CancelableTask(on_foreground ? job->isolate_->cancelable_task_manager() 2277 : &job->background_task_manager_), 2278 job_(job), 2279 on_foreground_(on_foreground) {} 2280 2281 ~CompileTask() { 2282 if (job_ != nullptr && on_foreground_) ResetPendingForegroundTask(); 2283 } 2284 2285 void RunInternal() final { 2286 if (!job_) return; 2287 if (on_foreground_) ResetPendingForegroundTask(); 2288 job_->step_->Run(on_foreground_); 2289 // After execution, reset {job_} such that we don't try to reset the pending 2290 // foreground task when the task is deleted. 2291 job_ = nullptr; 2292 } 2293 2294 void Cancel() { 2295 DCHECK_NOT_NULL(job_); 2296 job_ = nullptr; 2297 } 2298 2299 private: 2300 // {job_} will be cleared to cancel a pending task. 2301 AsyncCompileJob* job_; 2302 bool on_foreground_; 2303 2304 void ResetPendingForegroundTask() const { 2305 DCHECK_EQ(this, job_->pending_foreground_task_); 2306 job_->pending_foreground_task_ = nullptr; 2307 } 2308 }; 2309 2310 void AsyncCompileJob::StartForegroundTask() { 2311 DCHECK_NULL(pending_foreground_task_); 2312 2313 auto new_task = base::make_unique<CompileTask>(this, true); 2314 pending_foreground_task_ = new_task.get(); 2315 foreground_task_runner_->PostTask(std::move(new_task)); 2316 } 2317 2318 void AsyncCompileJob::ExecuteForegroundTaskImmediately() { 2319 DCHECK_NULL(pending_foreground_task_); 2320 2321 auto new_task = base::make_unique<CompileTask>(this, true); 2322 pending_foreground_task_ = new_task.get(); 2323 new_task->Run(); 2324 } 2325 2326 void AsyncCompileJob::CancelPendingForegroundTask() { 2327 if (!pending_foreground_task_) return; 2328 pending_foreground_task_->Cancel(); 2329 pending_foreground_task_ = nullptr; 2330 } 2331 2332 void AsyncCompileJob::StartBackgroundTask() { 2333 auto task = base::make_unique<CompileTask>(this, false); 2334 2335 // If --wasm-num-compilation-tasks=0 is passed, do only spawn foreground 2336 // tasks. This is used to make timing deterministic. 2337 if (FLAG_wasm_num_compilation_tasks > 0) { 2338 V8::GetCurrentPlatform()->CallOnWorkerThread(std::move(task)); 2339 } else { 2340 foreground_task_runner_->PostTask(std::move(task)); 2341 } 2342 } 2343 2344 template <typename Step, typename... Args> 2345 void AsyncCompileJob::DoSync(Args&&... args) { 2346 NextStep<Step>(std::forward<Args>(args)...); 2347 StartForegroundTask(); 2348 } 2349 2350 template <typename Step, typename... Args> 2351 void AsyncCompileJob::DoImmediately(Args&&... args) { 2352 NextStep<Step>(std::forward<Args>(args)...); 2353 ExecuteForegroundTaskImmediately(); 2354 } 2355 2356 template <typename Step, typename... Args> 2357 void AsyncCompileJob::DoAsync(Args&&... args) { 2358 NextStep<Step>(std::forward<Args>(args)...); 2359 StartBackgroundTask(); 2360 } 2361 2362 template <typename Step, typename... Args> 2363 void AsyncCompileJob::NextStep(Args&&... args) { 2364 step_.reset(new Step(std::forward<Args>(args)...)); 2365 step_->job_ = this; 2366 } 2367 2368 //========================================================================== 2369 // Step 1: (async) Decode the module. 2370 //========================================================================== 2371 class AsyncCompileJob::DecodeModule : public AsyncCompileJob::CompileStep { 2372 public: 2373 void RunInBackground() override { 2374 ModuleResult result; 2375 { 2376 DisallowHandleAllocation no_handle; 2377 DisallowHeapAllocation no_allocation; 2378 // Decode the module bytes. 2379 TRACE_COMPILE("(1) Decoding module...\n"); 2380 result = 2381 DecodeWasmModule(job_->enabled_features_, job_->wire_bytes_.start(), 2382 job_->wire_bytes_.end(), false, kWasmOrigin, 2383 job_->async_counters().get(), 2384 job_->isolate()->wasm_engine()->allocator()); 2385 } 2386 if (result.failed()) { 2387 // Decoding failure; reject the promise and clean up. 2388 job_->DoSync<DecodeFail>(std::move(result)); 2389 } else { 2390 // Decode passed. 2391 job_->DoSync<PrepareAndStartCompile>(std::move(result.val), true); 2392 } 2393 } 2394 }; 2395 2396 //========================================================================== 2397 // Step 1b: (sync) Fail decoding the module. 2398 //========================================================================== 2399 class AsyncCompileJob::DecodeFail : public CompileStep { 2400 public: 2401 explicit DecodeFail(ModuleResult result) : result_(std::move(result)) {} 2402 2403 private: 2404 ModuleResult result_; 2405 void RunInForeground() override { 2406 TRACE_COMPILE("(1b) Decoding failed.\n"); 2407 ErrorThrower thrower(job_->isolate_, "AsyncCompile"); 2408 thrower.CompileFailed("Wasm decoding failed", result_); 2409 // {job_} is deleted in AsyncCompileFailed, therefore the {return}. 2410 return job_->AsyncCompileFailed(thrower.Reify()); 2411 } 2412 }; 2413 2414 //========================================================================== 2415 // Step 2 (sync): Create heap-allocated data and start compile. 2416 //========================================================================== 2417 class AsyncCompileJob::PrepareAndStartCompile : public CompileStep { 2418 public: 2419 PrepareAndStartCompile(std::shared_ptr<const WasmModule> module, 2420 bool start_compilation) 2421 : module_(module), start_compilation_(start_compilation) {} 2422 2423 private: 2424 std::shared_ptr<const WasmModule> module_; 2425 bool start_compilation_; 2426 2427 void RunInForeground() override { 2428 TRACE_COMPILE("(2) Prepare and start compile...\n"); 2429 2430 // Make sure all compilation tasks stopped running. Decoding (async step) 2431 // is done. 2432 job_->background_task_manager_.CancelAndWait(); 2433 2434 // Embedder usage count for declared shared memories. 2435 if (module_->has_shared_memory) { 2436 job_->isolate_->CountUsage( 2437 v8::Isolate::UseCounterFeature::kWasmSharedMemory); 2438 } 2439 2440 // Create heap objects for script and module bytes to be stored in the 2441 // module object. Asm.js is not compiled asynchronously. 2442 Handle<Script> script = CreateWasmScript(job_->isolate_, job_->wire_bytes_); 2443 Handle<ByteArray> asm_js_offset_table; 2444 2445 const WasmModule* module = module_.get(); 2446 ModuleEnv env = CreateDefaultModuleEnv(module); 2447 // TODO(wasm): Improve efficiency of storing module wire bytes. Only store 2448 // relevant sections, not function bodies 2449 2450 // Create the module object and populate with compiled functions and 2451 // information needed at instantiation time. 2452 // TODO(clemensh): For the same module (same bytes / same hash), we should 2453 // only have one {WasmModuleObject}. Otherwise, we might only set 2454 // breakpoints on a (potentially empty) subset of the instances. 2455 // Create the module object. 2456 job_->module_object_ = WasmModuleObject::New( 2457 job_->isolate_, job_->enabled_features_, module_, env, 2458 {std::move(job_->bytes_copy_), job_->wire_bytes_.length()}, script, 2459 asm_js_offset_table); 2460 job_->native_module_ = job_->module_object_->native_module(); 2461 2462 { 2463 DeferredHandleScope deferred(job_->isolate_); 2464 job_->module_object_ = handle(*job_->module_object_, job_->isolate_); 2465 job_->deferred_handles_.push_back(deferred.Detach()); 2466 } 2467 size_t num_functions = 2468 module->functions.size() - module->num_imported_functions; 2469 2470 if (num_functions == 0) { 2471 // Tiering has nothing to do if module is empty. 2472 job_->tiering_completed_ = true; 2473 2474 // Degenerate case of an empty module. 2475 job_->FinishCompile(); 2476 return; 2477 } 2478 2479 CompilationState* compilation_state = 2480 job_->native_module_->compilation_state(); 2481 { 2482 // Instance field {job_} cannot be captured by copy, therefore 2483 // we need to add a local helper variable {job}. We want to 2484 // capture the {job} pointer by copy, as it otherwise is dependent 2485 // on the current step we are in. 2486 AsyncCompileJob* job = job_; 2487 compilation_state->SetCallback( 2488 [job](CompilationEvent event, ErrorThrower* thrower) { 2489 // Callback is called from a foreground thread. 2490 switch (event) { 2491 case CompilationEvent::kFinishedBaselineCompilation: 2492 if (job->DecrementAndCheckFinisherCount()) { 2493 SaveContext saved_context(job->isolate()); 2494 job->isolate()->set_context(*job->native_context_); 2495 job->FinishCompile(); 2496 } 2497 return; 2498 case CompilationEvent::kFinishedTopTierCompilation: 2499 // If a foreground task or a finisher is pending, we rely on 2500 // FinishModule to remove the job. 2501 if (job->pending_foreground_task_ || 2502 job->outstanding_finishers_.load() > 0) { 2503 job->tiering_completed_ = true; 2504 return; 2505 } 2506 job->isolate_->wasm_engine()->RemoveCompileJob(job); 2507 return; 2508 case CompilationEvent::kFailedCompilation: { 2509 // Tier-up compilation should not fail if baseline compilation 2510 // did not fail. 2511 DCHECK(!job->native_module_->compilation_state() 2512 ->baseline_compilation_finished()); 2513 2514 SaveContext saved_context(job->isolate()); 2515 job->isolate()->set_context(*job->native_context_); 2516 Handle<Object> error = thrower->Reify(); 2517 2518 DeferredHandleScope deferred(job->isolate()); 2519 error = handle(*error, job->isolate()); 2520 job->deferred_handles_.push_back(deferred.Detach()); 2521 job->DoSync<CompileFailed>(error); 2522 return; 2523 } 2524 } 2525 UNREACHABLE(); 2526 }); 2527 } 2528 if (start_compilation_) { 2529 // TODO(ahaas): Try to remove the {start_compilation_} check when 2530 // streaming decoding is done in the background. If 2531 // InitializeCompilationUnits always returns 0 for streaming compilation, 2532 // then DoAsync would do the same as NextStep already. 2533 2534 compilation_state->SetNumberOfFunctionsToCompile( 2535 module->num_declared_functions); 2536 // Add compilation units and kick off compilation. 2537 InitializeCompilationUnits(job_->native_module_); 2538 } 2539 } 2540 }; 2541 2542 //========================================================================== 2543 // Step 4b (sync): Compilation failed. Reject Promise. 2544 //========================================================================== 2545 class AsyncCompileJob::CompileFailed : public CompileStep { 2546 public: 2547 explicit CompileFailed(Handle<Object> error_reason) 2548 : error_reason_(error_reason) {} 2549 2550 void RunInForeground() override { 2551 TRACE_COMPILE("(4b) Compilation Failed...\n"); 2552 return job_->AsyncCompileFailed(error_reason_); 2553 } 2554 2555 private: 2556 Handle<Object> error_reason_; 2557 }; 2558 2559 //========================================================================== 2560 // Step 5 (sync): Compile JS->wasm wrappers. 2561 //========================================================================== 2562 class AsyncCompileJob::CompileWrappers : public CompileStep { 2563 // TODO(wasm): Compile all wrappers here, including the start function wrapper 2564 // and the wrappers for the function table elements. 2565 void RunInForeground() override { 2566 TRACE_COMPILE("(5) Compile wrappers...\n"); 2567 // TODO(6792): No longer needed once WebAssembly code is off heap. 2568 CodeSpaceMemoryModificationScope modification_scope(job_->isolate_->heap()); 2569 // Compile JS->wasm wrappers for exported functions. 2570 CompileJsToWasmWrappers(job_->isolate_, job_->module_object_); 2571 job_->DoSync<FinishModule>(); 2572 } 2573 }; 2574 2575 //========================================================================== 2576 // Step 6 (sync): Finish the module and resolve the promise. 2577 //========================================================================== 2578 class AsyncCompileJob::FinishModule : public CompileStep { 2579 void RunInForeground() override { 2580 TRACE_COMPILE("(6) Finish module...\n"); 2581 job_->AsyncCompileSucceeded(job_->module_object_); 2582 2583 size_t num_functions = job_->native_module_->num_functions() - 2584 job_->native_module_->num_imported_functions(); 2585 if (job_->native_module_->compilation_state()->compile_mode() == 2586 CompileMode::kRegular || 2587 num_functions == 0) { 2588 // If we do not tier up, the async compile job is done here and 2589 // can be deleted. 2590 job_->isolate_->wasm_engine()->RemoveCompileJob(job_); 2591 return; 2592 } 2593 // If background tiering compilation finished before we resolved the 2594 // promise, switch to patching now. Otherwise, patching will be scheduled 2595 // by a callback. 2596 DCHECK_EQ(CompileMode::kTiering, 2597 job_->native_module_->compilation_state()->compile_mode()); 2598 if (job_->tiering_completed_) { 2599 job_->isolate_->wasm_engine()->RemoveCompileJob(job_); 2600 } 2601 } 2602 }; 2603 2604 AsyncStreamingProcessor::AsyncStreamingProcessor(AsyncCompileJob* job) 2605 : decoder_(job->enabled_features_), 2606 job_(job), 2607 compilation_unit_builder_(nullptr) {} 2608 2609 void AsyncStreamingProcessor::FinishAsyncCompileJobWithError(ResultBase error) { 2610 // Make sure all background tasks stopped executing before we change the state 2611 // of the AsyncCompileJob to DecodeFail. 2612 job_->background_task_manager_.CancelAndWait(); 2613 2614 // Create a ModuleResult from the result we got as parameter. Since there was 2615 // no error, we don't have to provide a real wasm module to the ModuleResult. 2616 ModuleResult result(nullptr); 2617 result.MoveErrorFrom(error); 2618 2619 // Check if there is already a CompiledModule, in which case we have to clean 2620 // up the CompilationState as well. 2621 if (job_->native_module_) { 2622 job_->native_module_->compilation_state()->Abort(); 2623 2624 if (job_->pending_foreground_task_ == nullptr) { 2625 job_->DoSync<AsyncCompileJob::DecodeFail>(std::move(result)); 2626 } else { 2627 job_->NextStep<AsyncCompileJob::DecodeFail>(std::move(result)); 2628 } 2629 2630 // Clear the {compilation_unit_builder_} if it exists. This is needed 2631 // because there is a check in the destructor of the 2632 // {CompilationUnitBuilder} that it is empty. 2633 if (compilation_unit_builder_) compilation_unit_builder_->Clear(); 2634 } else { 2635 job_->DoSync<AsyncCompileJob::DecodeFail>(std::move(result)); 2636 } 2637 } 2638 2639 // Process the module header. 2640 bool AsyncStreamingProcessor::ProcessModuleHeader(Vector<const uint8_t> bytes, 2641 uint32_t offset) { 2642 TRACE_STREAMING("Process module header...\n"); 2643 decoder_.StartDecoding(job_->async_counters().get(), 2644 job_->isolate()->wasm_engine()->allocator()); 2645 decoder_.DecodeModuleHeader(bytes, offset); 2646 if (!decoder_.ok()) { 2647 FinishAsyncCompileJobWithError(decoder_.FinishDecoding(false)); 2648 return false; 2649 } 2650 return true; 2651 } 2652 2653 // Process all sections except for the code section. 2654 bool AsyncStreamingProcessor::ProcessSection(SectionCode section_code, 2655 Vector<const uint8_t> bytes, 2656 uint32_t offset) { 2657 TRACE_STREAMING("Process section %d ...\n", section_code); 2658 if (compilation_unit_builder_) { 2659 // We reached a section after the code section, we do not need the 2660 // compilation_unit_builder_ anymore. 2661 CommitCompilationUnits(); 2662 compilation_unit_builder_.reset(); 2663 } 2664 if (section_code == SectionCode::kUnknownSectionCode) { 2665 Decoder decoder(bytes, offset); 2666 section_code = ModuleDecoder::IdentifyUnknownSection( 2667 decoder, bytes.start() + bytes.length()); 2668 if (section_code == SectionCode::kUnknownSectionCode) { 2669 // Skip unknown sections that we do not know how to handle. 2670 return true; 2671 } 2672 // Remove the unknown section tag from the payload bytes. 2673 offset += decoder.position(); 2674 bytes = bytes.SubVector(decoder.position(), bytes.size()); 2675 } 2676 constexpr bool verify_functions = false; 2677 decoder_.DecodeSection(section_code, bytes, offset, verify_functions); 2678 if (!decoder_.ok()) { 2679 FinishAsyncCompileJobWithError(decoder_.FinishDecoding(false)); 2680 return false; 2681 } 2682 return true; 2683 } 2684 2685 // Start the code section. 2686 bool AsyncStreamingProcessor::ProcessCodeSectionHeader(size_t functions_count, 2687 uint32_t offset) { 2688 TRACE_STREAMING("Start the code section with %zu functions...\n", 2689 functions_count); 2690 if (!decoder_.CheckFunctionsCount(static_cast<uint32_t>(functions_count), 2691 offset)) { 2692 FinishAsyncCompileJobWithError(decoder_.FinishDecoding(false)); 2693 return false; 2694 } 2695 // Execute the PrepareAndStartCompile step immediately and not in a separate 2696 // task. 2697 job_->DoImmediately<AsyncCompileJob::PrepareAndStartCompile>( 2698 decoder_.shared_module(), false); 2699 2700 job_->native_module_->compilation_state()->SetNumberOfFunctionsToCompile( 2701 functions_count); 2702 2703 // Set outstanding_finishers_ to 2, because both the AsyncCompileJob and the 2704 // AsyncStreamingProcessor have to finish. 2705 job_->outstanding_finishers_.store(2); 2706 compilation_unit_builder_.reset( 2707 new CompilationUnitBuilder(job_->native_module_)); 2708 return true; 2709 } 2710 2711 // Process a function body. 2712 bool AsyncStreamingProcessor::ProcessFunctionBody(Vector<const uint8_t> bytes, 2713 uint32_t offset) { 2714 TRACE_STREAMING("Process function body %d ...\n", next_function_); 2715 2716 decoder_.DecodeFunctionBody( 2717 next_function_, static_cast<uint32_t>(bytes.length()), offset, false); 2718 2719 uint32_t index = next_function_ + decoder_.module()->num_imported_functions; 2720 const WasmFunction* func = &decoder_.module()->functions[index]; 2721 WasmName name = {nullptr, 0}; 2722 compilation_unit_builder_->AddUnit(func, offset, bytes, name); 2723 ++next_function_; 2724 // This method always succeeds. The return value is necessary to comply with 2725 // the StreamingProcessor interface. 2726 return true; 2727 } 2728 2729 void AsyncStreamingProcessor::CommitCompilationUnits() { 2730 DCHECK(compilation_unit_builder_); 2731 compilation_unit_builder_->Commit(); 2732 } 2733 2734 void AsyncStreamingProcessor::OnFinishedChunk() { 2735 TRACE_STREAMING("FinishChunk...\n"); 2736 if (compilation_unit_builder_) CommitCompilationUnits(); 2737 } 2738 2739 // Finish the processing of the stream. 2740 void AsyncStreamingProcessor::OnFinishedStream(OwnedVector<uint8_t> bytes) { 2741 TRACE_STREAMING("Finish stream...\n"); 2742 ModuleResult result = decoder_.FinishDecoding(false); 2743 DCHECK(result.ok()); 2744 bool needs_finish = job_->DecrementAndCheckFinisherCount(); 2745 if (job_->native_module_ == nullptr) { 2746 // We are processing a WebAssembly module without code section. We need to 2747 // prepare compilation first before we can finish it. 2748 // {PrepareAndStartCompile} will call {FinishCompile} by itself if there 2749 // is no code section. 2750 DCHECK(needs_finish); 2751 needs_finish = false; 2752 job_->DoImmediately<AsyncCompileJob::PrepareAndStartCompile>(result.val, 2753 true); 2754 } 2755 job_->wire_bytes_ = ModuleWireBytes(bytes.as_vector()); 2756 job_->native_module_->set_wire_bytes(std::move(bytes)); 2757 if (needs_finish) { 2758 HandleScope scope(job_->isolate_); 2759 SaveContext saved_context(job_->isolate_); 2760 job_->isolate_->set_context(*job_->native_context_); 2761 job_->FinishCompile(); 2762 } 2763 } 2764 2765 // Report an error detected in the StreamingDecoder. 2766 void AsyncStreamingProcessor::OnError(DecodeResult result) { 2767 TRACE_STREAMING("Stream error...\n"); 2768 FinishAsyncCompileJobWithError(std::move(result)); 2769 } 2770 2771 void AsyncStreamingProcessor::OnAbort() { 2772 TRACE_STREAMING("Abort stream...\n"); 2773 job_->Abort(); 2774 } 2775 2776 void CompilationStateDeleter::operator()( 2777 CompilationState* compilation_state) const { 2778 delete compilation_state; 2779 } 2780 2781 std::unique_ptr<CompilationState, CompilationStateDeleter> NewCompilationState( 2782 Isolate* isolate, const ModuleEnv& env) { 2783 return std::unique_ptr<CompilationState, CompilationStateDeleter>( 2784 new CompilationState(isolate, env)); 2785 } 2786 2787 ModuleEnv* GetModuleEnv(CompilationState* compilation_state) { 2788 return compilation_state->module_env(); 2789 } 2790 2791 CompilationState::CompilationState(internal::Isolate* isolate, 2792 const ModuleEnv& env) 2793 : isolate_(isolate), 2794 wasm_engine_(isolate->wasm_engine()), 2795 module_env_(env), 2796 compile_mode_(FLAG_wasm_tier_up && env.module->origin == kWasmOrigin 2797 ? CompileMode::kTiering 2798 : CompileMode::kRegular), 2799 max_background_tasks_(std::max( 2800 1, std::min(FLAG_wasm_num_compilation_tasks, 2801 V8::GetCurrentPlatform()->NumberOfWorkerThreads()))) { 2802 v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate_); 2803 v8::Platform* platform = V8::GetCurrentPlatform(); 2804 foreground_task_runner_ = platform->GetForegroundTaskRunner(v8_isolate); 2805 } 2806 2807 CompilationState::~CompilationState() { 2808 background_task_manager_.CancelAndWait(); 2809 foreground_task_manager_.CancelAndWait(); 2810 } 2811 2812 void CompilationState::SetNumberOfFunctionsToCompile(size_t num_functions) { 2813 DCHECK(!failed()); 2814 outstanding_units_ = num_functions; 2815 2816 if (compile_mode_ == CompileMode::kTiering) { 2817 outstanding_units_ += num_functions; 2818 num_tiering_units_ = num_functions; 2819 } 2820 } 2821 2822 void CompilationState::SetCallback( 2823 std::function<void(CompilationEvent, ErrorThrower*)> callback) { 2824 DCHECK_NULL(callback_); 2825 callback_ = callback; 2826 } 2827 2828 void CompilationState::AddCompilationUnits( 2829 std::vector<std::unique_ptr<WasmCompilationUnit>>& baseline_units, 2830 std::vector<std::unique_ptr<WasmCompilationUnit>>& tiering_units) { 2831 { 2832 base::LockGuard<base::Mutex> guard(&mutex_); 2833 2834 if (compile_mode_ == CompileMode::kTiering) { 2835 DCHECK_EQ(baseline_units.size(), tiering_units.size()); 2836 DCHECK_EQ(tiering_units.back()->mode(), ExecutionTier::kOptimized); 2837 tiering_compilation_units_.insert( 2838 tiering_compilation_units_.end(), 2839 std::make_move_iterator(tiering_units.begin()), 2840 std::make_move_iterator(tiering_units.end())); 2841 } else { 2842 DCHECK(tiering_compilation_units_.empty()); 2843 } 2844 2845 baseline_compilation_units_.insert( 2846 baseline_compilation_units_.end(), 2847 std::make_move_iterator(baseline_units.begin()), 2848 std::make_move_iterator(baseline_units.end())); 2849 } 2850 2851 RestartBackgroundTasks(); 2852 } 2853 2854 std::unique_ptr<WasmCompilationUnit> 2855 CompilationState::GetNextCompilationUnit() { 2856 base::LockGuard<base::Mutex> guard(&mutex_); 2857 2858 std::vector<std::unique_ptr<WasmCompilationUnit>>& units = 2859 baseline_compilation_units_.empty() ? tiering_compilation_units_ 2860 : baseline_compilation_units_; 2861 2862 if (!units.empty()) { 2863 std::unique_ptr<WasmCompilationUnit> unit = std::move(units.back()); 2864 units.pop_back(); 2865 return unit; 2866 } 2867 2868 return std::unique_ptr<WasmCompilationUnit>(); 2869 } 2870 2871 std::unique_ptr<WasmCompilationUnit> CompilationState::GetNextExecutedUnit() { 2872 base::LockGuard<base::Mutex> guard(&mutex_); 2873 std::vector<std::unique_ptr<WasmCompilationUnit>>& units = finish_units(); 2874 if (units.empty()) return {}; 2875 std::unique_ptr<WasmCompilationUnit> ret = std::move(units.back()); 2876 units.pop_back(); 2877 return ret; 2878 } 2879 2880 bool CompilationState::HasCompilationUnitToFinish() { 2881 base::LockGuard<base::Mutex> guard(&mutex_); 2882 return !finish_units().empty(); 2883 } 2884 2885 void CompilationState::OnError(ErrorThrower* thrower) { 2886 Abort(); 2887 DCHECK(thrower->error()); 2888 NotifyOnEvent(CompilationEvent::kFailedCompilation, thrower); 2889 } 2890 2891 void CompilationState::OnFinishedUnit() { 2892 DCHECK_GT(outstanding_units_, 0); 2893 --outstanding_units_; 2894 2895 if (outstanding_units_ == 0) { 2896 background_task_manager_.CancelAndWait(); 2897 baseline_compilation_finished_ = true; 2898 2899 DCHECK(compile_mode_ == CompileMode::kRegular || 2900 compile_mode_ == CompileMode::kTiering); 2901 NotifyOnEvent(compile_mode_ == CompileMode::kRegular 2902 ? CompilationEvent::kFinishedBaselineCompilation 2903 : CompilationEvent::kFinishedTopTierCompilation, 2904 nullptr); 2905 2906 } else if (outstanding_units_ == num_tiering_units_) { 2907 DCHECK_EQ(compile_mode_, CompileMode::kTiering); 2908 baseline_compilation_finished_ = true; 2909 2910 // TODO(wasm): For streaming compilation, we want to start top tier 2911 // compilation before all functions have been compiled with Liftoff, e.g. 2912 // in the case when all received functions have been compiled with Liftoff 2913 // and we are waiting for new functions to compile. 2914 2915 // If we are in {kRegular} mode, {num_tiering_units_} is 0, therefore 2916 // this case is already caught by the previous check. 2917 NotifyOnEvent(CompilationEvent::kFinishedBaselineCompilation, nullptr); 2918 RestartBackgroundTasks(); 2919 } 2920 } 2921 2922 void CompilationState::ScheduleUnitForFinishing( 2923 std::unique_ptr<WasmCompilationUnit> unit, ExecutionTier mode) { 2924 base::LockGuard<base::Mutex> guard(&mutex_); 2925 if (compile_mode_ == CompileMode::kTiering && 2926 mode == ExecutionTier::kOptimized) { 2927 tiering_finish_units_.push_back(std::move(unit)); 2928 } else { 2929 baseline_finish_units_.push_back(std::move(unit)); 2930 } 2931 2932 if (!finisher_is_running_ && !failed_) { 2933 ScheduleFinisherTask(); 2934 // We set the flag here so that not more than one finisher is started. 2935 finisher_is_running_ = true; 2936 } 2937 } 2938 2939 void CompilationState::OnBackgroundTaskStopped(const WasmFeatures& detected) { 2940 base::LockGuard<base::Mutex> guard(&mutex_); 2941 DCHECK_LE(1, num_background_tasks_); 2942 --num_background_tasks_; 2943 UnionFeaturesInto(&detected_features_, detected); 2944 } 2945 2946 void CompilationState::PublishDetectedFeatures(Isolate* isolate, 2947 const WasmFeatures& detected) { 2948 // Notifying the isolate of the feature counts must take place under 2949 // the mutex, because even if we have finished baseline compilation, 2950 // tiering compilations may still occur in the background. 2951 base::LockGuard<base::Mutex> guard(&mutex_); 2952 UnionFeaturesInto(&detected_features_, detected); 2953 UpdateFeatureUseCounts(isolate, detected_features_); 2954 } 2955 2956 void CompilationState::RestartBackgroundTasks(size_t max) { 2957 size_t num_restart; 2958 { 2959 base::LockGuard<base::Mutex> guard(&mutex_); 2960 // No need to restart tasks if compilation already failed. 2961 if (failed_) return; 2962 2963 DCHECK_LE(num_background_tasks_, max_background_tasks_); 2964 if (num_background_tasks_ == max_background_tasks_) return; 2965 size_t num_compilation_units = 2966 baseline_compilation_units_.size() + tiering_compilation_units_.size(); 2967 size_t stopped_tasks = max_background_tasks_ - num_background_tasks_; 2968 num_restart = std::min(max, std::min(num_compilation_units, stopped_tasks)); 2969 num_background_tasks_ += num_restart; 2970 } 2971 2972 for (; num_restart > 0; --num_restart) { 2973 auto task = base::make_unique<BackgroundCompileTask>( 2974 this, &background_task_manager_); 2975 2976 // If --wasm-num-compilation-tasks=0 is passed, do only spawn foreground 2977 // tasks. This is used to make timing deterministic. 2978 if (FLAG_wasm_num_compilation_tasks > 0) { 2979 V8::GetCurrentPlatform()->CallOnWorkerThread(std::move(task)); 2980 } else { 2981 foreground_task_runner_->PostTask(std::move(task)); 2982 } 2983 } 2984 } 2985 2986 bool CompilationState::SetFinisherIsRunning(bool value) { 2987 base::LockGuard<base::Mutex> guard(&mutex_); 2988 if (finisher_is_running_ == value) return false; 2989 finisher_is_running_ = value; 2990 return true; 2991 } 2992 2993 void CompilationState::ScheduleFinisherTask() { 2994 foreground_task_runner_->PostTask( 2995 base::make_unique<FinishCompileTask>(this, &foreground_task_manager_)); 2996 } 2997 2998 void CompilationState::Abort() { 2999 { 3000 base::LockGuard<base::Mutex> guard(&mutex_); 3001 failed_ = true; 3002 } 3003 background_task_manager_.CancelAndWait(); 3004 } 3005 3006 void CompilationState::NotifyOnEvent(CompilationEvent event, 3007 ErrorThrower* thrower) { 3008 if (callback_) callback_(event, thrower); 3009 } 3010 3011 void CompileJsToWasmWrappers(Isolate* isolate, 3012 Handle<WasmModuleObject> module_object) { 3013 JSToWasmWrapperCache js_to_wasm_cache; 3014 int wrapper_index = 0; 3015 Handle<FixedArray> export_wrappers(module_object->export_wrappers(), isolate); 3016 NativeModule* native_module = module_object->native_module(); 3017 UseTrapHandler use_trap_handler = 3018 native_module->use_trap_handler() ? kUseTrapHandler : kNoTrapHandler; 3019 const WasmModule* module = native_module->module(); 3020 for (auto exp : module->export_table) { 3021 if (exp.kind != kExternalFunction) continue; 3022 Handle<Code> wrapper_code = js_to_wasm_cache.GetOrCompileJSToWasmWrapper( 3023 isolate, native_module, exp.index, use_trap_handler); 3024 export_wrappers->set(wrapper_index, *wrapper_code); 3025 RecordStats(*wrapper_code, isolate->counters()); 3026 ++wrapper_index; 3027 } 3028 } 3029 3030 Handle<Script> CreateWasmScript(Isolate* isolate, 3031 const ModuleWireBytes& wire_bytes) { 3032 Handle<Script> script = 3033 isolate->factory()->NewScript(isolate->factory()->empty_string()); 3034 script->set_context_data(isolate->native_context()->debug_context_id()); 3035 script->set_type(Script::TYPE_WASM); 3036 3037 int hash = StringHasher::HashSequentialString( 3038 reinterpret_cast<const char*>(wire_bytes.start()), 3039 static_cast<int>(wire_bytes.length()), kZeroHashSeed); 3040 3041 const int kBufferSize = 32; 3042 char buffer[kBufferSize]; 3043 int url_chars = SNPrintF(ArrayVector(buffer), "wasm://wasm/%08x", hash); 3044 DCHECK(url_chars >= 0 && url_chars < kBufferSize); 3045 MaybeHandle<String> url_str = isolate->factory()->NewStringFromOneByte( 3046 Vector<const uint8_t>(reinterpret_cast<uint8_t*>(buffer), url_chars), 3047 TENURED); 3048 script->set_source_url(*url_str.ToHandleChecked()); 3049 3050 int name_chars = SNPrintF(ArrayVector(buffer), "wasm-%08x", hash); 3051 DCHECK(name_chars >= 0 && name_chars < kBufferSize); 3052 MaybeHandle<String> name_str = isolate->factory()->NewStringFromOneByte( 3053 Vector<const uint8_t>(reinterpret_cast<uint8_t*>(buffer), name_chars), 3054 TENURED); 3055 script->set_name(*name_str.ToHandleChecked()); 3056 3057 return script; 3058 } 3059 3060 } // namespace wasm 3061 } // namespace internal 3062 } // namespace v8 3063 3064 #undef TRACE 3065 #undef TRACE_COMPILE 3066 #undef TRACE_STREAMING 3067 #undef TRACE_LAZY 3068