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
isolate() const103 Isolate* isolate() const { return isolate_; }
104
failed() const105 bool failed() const {
106 base::LockGuard<base::Mutex> guard(&mutex_);
107 return failed_;
108 }
109
baseline_compilation_finished() const110 bool baseline_compilation_finished() const {
111 return baseline_compilation_finished_;
112 }
113
wasm_engine() const114 WasmEngine* wasm_engine() const { return wasm_engine_; }
compile_mode() const115 CompileMode compile_mode() const { return compile_mode_; }
module_env()116 ModuleEnv* module_env() { return &module_env_; }
detected_features()117 WasmFeatures* detected_features() { return &detected_features_; }
118
119 private:
120 void NotifyOnEvent(CompilationEvent event, ErrorThrower* thrower);
121
finish_units()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
UpdateFeatureUseCounts(Isolate * isolate,const WasmFeatures & detected)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:
GetOrCompileJSToWasmWrapper(Isolate * isolate,const NativeModule * native_module,uint32_t func_index,UseTrapHandler use_trap_handler)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
use_trap_handler() const252 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
InstantiateToInstanceObject(Isolate * isolate,ErrorThrower * thrower,Handle<WasmModuleObject> module_object,MaybeHandle<JSReceiver> imports,MaybeHandle<JSArrayBuffer> memory)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
LazyCompileFunction(Isolate * isolate,NativeModule * native_module,int func_index)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
CompileLazy(Isolate * isolate,NativeModule * native_module,uint32_t func_index)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 {
compile_lazy(const WasmModule * module)418 bool compile_lazy(const WasmModule* module) {
419 return FLAG_wasm_lazy_compilation ||
420 (FLAG_asm_wasm_lazy_compilation && module->origin == kAsmJsOrigin);
421 }
422
raw_buffer_ptr(MaybeHandle<JSArrayBuffer> buffer,int offset)423 byte* raw_buffer_ptr(MaybeHandle<JSArrayBuffer> buffer, int offset) {
424 return static_cast<byte*>(buffer.ToHandleChecked()->backing_store()) + offset;
425 }
426
RecordStats(const Code * code,Counters * counters)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
in_bounds(uint32_t offset,size_t size,size_t upper)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
MonotonicallyIncreasingTimeInMs()439 double MonotonicallyIncreasingTimeInMs() {
440 return V8::GetCurrentPlatform()->MonotonicallyIncreasingTime() *
441 base::Time::kMillisecondsPerSecond;
442 }
443
CreateDefaultModuleEnv(const WasmModule * module,bool allow_trap_handler=true)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:
CompilationUnitBuilder(NativeModule * native_module)458 explicit CompilationUnitBuilder(NativeModule* native_module)
459 : native_module_(native_module),
460 compilation_state_(native_module->compilation_state()) {}
461
AddUnit(const WasmFunction * function,uint32_t buffer_offset,Vector<const uint8_t> bytes,WasmName name)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
Commit()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
Clear()487 void Clear() {
488 baseline_units_.clear();
489 tiering_units_.clear();
490 }
491
492 private:
CreateUnit(const WasmFunction * function,uint32_t buffer_offset,Vector<const uint8_t> bytes,WasmName name,ExecutionTier mode)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.
FetchAndExecuteCompilationUnit(CompilationState * compilation_state,WasmFeatures * detected)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
InitializeCompilationUnits(NativeModule * native_module)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
FinishCompilationUnits(CompilationState * compilation_state,ErrorThrower * thrower)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
CompileInParallel(Isolate * isolate,NativeModule * native_module,Handle<WasmModuleObject> module_object,ErrorThrower * thrower)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
CompileSequentially(Isolate * isolate,NativeModule * native_module,ModuleEnv * module_env,ErrorThrower * thrower)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
ValidateSequentially(Isolate * isolate,NativeModule * native_module,ErrorThrower * thrower)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
CompileNativeModule(Isolate * isolate,ErrorThrower * thrower,Handle<WasmModuleObject> module_object,const WasmModule * wasm_module,ModuleEnv * env)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:
FinishCompileTask(CompilationState * compilation_state,CancelableTaskManager * task_manager)768 explicit FinishCompileTask(CompilationState* compilation_state,
769 CancelableTaskManager* task_manager)
770 : CancelableTask(task_manager), compilation_state_(compilation_state) {}
771
RunInternal()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:
BackgroundCompileTask(CompilationState * compilation_state,CancelableTaskManager * task_manager)846 explicit BackgroundCompileTask(CompilationState* compilation_state,
847 CancelableTaskManager* task_manager)
848 : CancelableTask(task_manager), compilation_state_(compilation_state) {}
849
RunInternal()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
CompileToModuleObject(Isolate * isolate,const WasmFeatures & enabled,ErrorThrower * thrower,std::shared_ptr<const WasmModule> module,const ModuleWireBytes & wire_bytes,Handle<Script> asm_js_script,Vector<const byte> asm_js_offset_table_bytes)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
InstanceBuilder(Isolate * isolate,ErrorThrower * thrower,Handle<WasmModuleObject> module_object,MaybeHandle<JSReceiver> ffi,MaybeHandle<JSArrayBuffer> memory)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.
Build()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
ExecuteStartFunction()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.
LookupImport(uint32_t index,Handle<String> module_name,Handle<String> import_name)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.
LookupImportAsm(uint32_t index,Handle<String> import_name)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
EvalUint32InitExpr(const WasmInitExpr & expr)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.
LoadDataSegments(Handle<WasmInstanceObject> instance)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
WriteGlobalValue(const WasmGlobal & global,double num)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
WriteGlobalValue(const WasmGlobal & global,Handle<WasmGlobalObject> value)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
SanitizeImports()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
FindImportedMemoryBuffer() const1446 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.
ProcessImports(Handle<WasmInstanceObject> instance)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>
GetRawGlobalPtr(const WasmGlobal & global)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.
InitGlobals()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.
AllocateMemory(uint32_t num_pages)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
NeedsWrappers() const1824 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.
ProcessExports(Handle<WasmInstanceObject> instance)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
InitializeTables(Handle<WasmInstanceObject> instance)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
LoadTableSegments(Handle<WasmInstanceObject> instance)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
AsyncCompileJob(Isolate * isolate,const WasmFeatures & enabled,std::unique_ptr<byte[]> bytes_copy,size_t length,Handle<Context> context,std::shared_ptr<CompilationResultResolver> resolver)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
Start()2150 void AsyncCompileJob::Start() {
2151 DoAsync<DecodeModule>(); // --
2152 }
2153
Abort()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
CreateStreamingDecoder()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
~AsyncCompileJob()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.
FinishCompile()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
AsyncCompileFailed(Handle<Object> error_reason)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
AsyncCompileSucceeded(Handle<WasmModuleObject> result)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:
~CompileStep()2250 virtual ~CompileStep() {}
2251
Run(bool on_foreground)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
RunInForeground()2263 virtual void RunInForeground() { UNREACHABLE(); }
RunInBackground()2264 virtual void RunInBackground() { UNREACHABLE(); }
2265
2266 AsyncCompileJob* job_ = nullptr;
2267 };
2268
2269 class AsyncCompileJob::CompileTask : public CancelableTask {
2270 public:
CompileTask(AsyncCompileJob * job,bool on_foreground)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
~CompileTask()2281 ~CompileTask() {
2282 if (job_ != nullptr && on_foreground_) ResetPendingForegroundTask();
2283 }
2284
RunInternal()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
Cancel()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
ResetPendingForegroundTask() const2304 void ResetPendingForegroundTask() const {
2305 DCHECK_EQ(this, job_->pending_foreground_task_);
2306 job_->pending_foreground_task_ = nullptr;
2307 }
2308 };
2309
StartForegroundTask()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
ExecuteForegroundTaskImmediately()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
CancelPendingForegroundTask()2326 void AsyncCompileJob::CancelPendingForegroundTask() {
2327 if (!pending_foreground_task_) return;
2328 pending_foreground_task_->Cancel();
2329 pending_foreground_task_ = nullptr;
2330 }
2331
StartBackgroundTask()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>
DoSync(Args &&...args)2345 void AsyncCompileJob::DoSync(Args&&... args) {
2346 NextStep<Step>(std::forward<Args>(args)...);
2347 StartForegroundTask();
2348 }
2349
2350 template <typename Step, typename... Args>
DoImmediately(Args &&...args)2351 void AsyncCompileJob::DoImmediately(Args&&... args) {
2352 NextStep<Step>(std::forward<Args>(args)...);
2353 ExecuteForegroundTaskImmediately();
2354 }
2355
2356 template <typename Step, typename... Args>
DoAsync(Args &&...args)2357 void AsyncCompileJob::DoAsync(Args&&... args) {
2358 NextStep<Step>(std::forward<Args>(args)...);
2359 StartBackgroundTask();
2360 }
2361
2362 template <typename Step, typename... Args>
NextStep(Args &&...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:
RunInBackground()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:
DecodeFail(ModuleResult result)2401 explicit DecodeFail(ModuleResult result) : result_(std::move(result)) {}
2402
2403 private:
2404 ModuleResult result_;
RunInForeground()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:
PrepareAndStartCompile(std::shared_ptr<const WasmModule> module,bool start_compilation)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
RunInForeground()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:
CompileFailed(Handle<Object> error_reason)2547 explicit CompileFailed(Handle<Object> error_reason)
2548 : error_reason_(error_reason) {}
2549
RunInForeground()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.
RunInForeground()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 {
RunInForeground()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
AsyncStreamingProcessor(AsyncCompileJob * job)2604 AsyncStreamingProcessor::AsyncStreamingProcessor(AsyncCompileJob* job)
2605 : decoder_(job->enabled_features_),
2606 job_(job),
2607 compilation_unit_builder_(nullptr) {}
2608
FinishAsyncCompileJobWithError(ResultBase error)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.
ProcessModuleHeader(Vector<const uint8_t> bytes,uint32_t offset)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.
ProcessSection(SectionCode section_code,Vector<const uint8_t> bytes,uint32_t offset)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.
ProcessCodeSectionHeader(size_t functions_count,uint32_t offset)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.
ProcessFunctionBody(Vector<const uint8_t> bytes,uint32_t offset)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
CommitCompilationUnits()2729 void AsyncStreamingProcessor::CommitCompilationUnits() {
2730 DCHECK(compilation_unit_builder_);
2731 compilation_unit_builder_->Commit();
2732 }
2733
OnFinishedChunk()2734 void AsyncStreamingProcessor::OnFinishedChunk() {
2735 TRACE_STREAMING("FinishChunk...\n");
2736 if (compilation_unit_builder_) CommitCompilationUnits();
2737 }
2738
2739 // Finish the processing of the stream.
OnFinishedStream(OwnedVector<uint8_t> bytes)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.
OnError(DecodeResult result)2766 void AsyncStreamingProcessor::OnError(DecodeResult result) {
2767 TRACE_STREAMING("Stream error...\n");
2768 FinishAsyncCompileJobWithError(std::move(result));
2769 }
2770
OnAbort()2771 void AsyncStreamingProcessor::OnAbort() {
2772 TRACE_STREAMING("Abort stream...\n");
2773 job_->Abort();
2774 }
2775
operator ()(CompilationState * compilation_state) const2776 void CompilationStateDeleter::operator()(
2777 CompilationState* compilation_state) const {
2778 delete compilation_state;
2779 }
2780
NewCompilationState(Isolate * isolate,const ModuleEnv & env)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
GetModuleEnv(CompilationState * compilation_state)2787 ModuleEnv* GetModuleEnv(CompilationState* compilation_state) {
2788 return compilation_state->module_env();
2789 }
2790
CompilationState(internal::Isolate * isolate,const ModuleEnv & env)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
~CompilationState()2807 CompilationState::~CompilationState() {
2808 background_task_manager_.CancelAndWait();
2809 foreground_task_manager_.CancelAndWait();
2810 }
2811
SetNumberOfFunctionsToCompile(size_t num_functions)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
SetCallback(std::function<void (CompilationEvent,ErrorThrower *)> callback)2822 void CompilationState::SetCallback(
2823 std::function<void(CompilationEvent, ErrorThrower*)> callback) {
2824 DCHECK_NULL(callback_);
2825 callback_ = callback;
2826 }
2827
AddCompilationUnits(std::vector<std::unique_ptr<WasmCompilationUnit>> & baseline_units,std::vector<std::unique_ptr<WasmCompilationUnit>> & tiering_units)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>
GetNextCompilationUnit()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
GetNextExecutedUnit()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
HasCompilationUnitToFinish()2880 bool CompilationState::HasCompilationUnitToFinish() {
2881 base::LockGuard<base::Mutex> guard(&mutex_);
2882 return !finish_units().empty();
2883 }
2884
OnError(ErrorThrower * thrower)2885 void CompilationState::OnError(ErrorThrower* thrower) {
2886 Abort();
2887 DCHECK(thrower->error());
2888 NotifyOnEvent(CompilationEvent::kFailedCompilation, thrower);
2889 }
2890
OnFinishedUnit()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
ScheduleUnitForFinishing(std::unique_ptr<WasmCompilationUnit> unit,ExecutionTier mode)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
OnBackgroundTaskStopped(const WasmFeatures & detected)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
PublishDetectedFeatures(Isolate * isolate,const WasmFeatures & detected)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
RestartBackgroundTasks(size_t max)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
SetFinisherIsRunning(bool value)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
ScheduleFinisherTask()2993 void CompilationState::ScheduleFinisherTask() {
2994 foreground_task_runner_->PostTask(
2995 base::make_unique<FinishCompileTask>(this, &foreground_task_manager_));
2996 }
2997
Abort()2998 void CompilationState::Abort() {
2999 {
3000 base::LockGuard<base::Mutex> guard(&mutex_);
3001 failed_ = true;
3002 }
3003 background_task_manager_.CancelAndWait();
3004 }
3005
NotifyOnEvent(CompilationEvent event,ErrorThrower * thrower)3006 void CompilationState::NotifyOnEvent(CompilationEvent event,
3007 ErrorThrower* thrower) {
3008 if (callback_) callback_(event, thrower);
3009 }
3010
CompileJsToWasmWrappers(Isolate * isolate,Handle<WasmModuleObject> module_object)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
CreateWasmScript(Isolate * isolate,const ModuleWireBytes & wire_bytes)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