• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2015 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/base/atomic-utils.h"
6 #include "src/macro-assembler.h"
7 #include "src/objects.h"
8 #include "src/property-descriptor.h"
9 #include "src/v8.h"
10 
11 #include "src/simulator.h"
12 
13 #include "src/wasm/ast-decoder.h"
14 #include "src/wasm/module-decoder.h"
15 #include "src/wasm/wasm-debug.h"
16 #include "src/wasm/wasm-function-name-table.h"
17 #include "src/wasm/wasm-module.h"
18 #include "src/wasm/wasm-result.h"
19 
20 #include "src/compiler/wasm-compiler.h"
21 
22 namespace v8 {
23 namespace internal {
24 namespace wasm {
25 
26 static const int kPlaceholderMarker = 1000000000;
27 
28 static const char* wasmSections[] = {
29 #define F(enumerator, order, string) string,
30     FOR_EACH_WASM_SECTION_TYPE(F)
31 #undef F
32         "<unknown>"  // entry for "Max"
33 };
34 
35 static uint8_t wasmSectionsLengths[]{
36 #define F(enumerator, order, string) sizeof(string) - 1,
37     FOR_EACH_WASM_SECTION_TYPE(F)
38 #undef F
39         9  // entry for "Max"
40 };
41 
42 static uint8_t wasmSectionsOrders[]{
43 #define F(enumerator, order, string) order,
44     FOR_EACH_WASM_SECTION_TYPE(F)
45 #undef F
46         0  // entry for "Max"
47 };
48 
49 static_assert(sizeof(wasmSections) / sizeof(wasmSections[0]) ==
50                   (size_t)WasmSection::Code::Max + 1,
51               "expected enum WasmSection::Code to be monotonic from 0");
52 
begin()53 WasmSection::Code WasmSection::begin() { return (WasmSection::Code)0; }
end()54 WasmSection::Code WasmSection::end() { return WasmSection::Code::Max; }
next(WasmSection::Code code)55 WasmSection::Code WasmSection::next(WasmSection::Code code) {
56   return (WasmSection::Code)(1 + (uint32_t)code);
57 }
58 
getName(WasmSection::Code code)59 const char* WasmSection::getName(WasmSection::Code code) {
60   return wasmSections[(size_t)code];
61 }
62 
getNameLength(WasmSection::Code code)63 size_t WasmSection::getNameLength(WasmSection::Code code) {
64   return wasmSectionsLengths[(size_t)code];
65 }
66 
getOrder(WasmSection::Code code)67 int WasmSection::getOrder(WasmSection::Code code) {
68   return wasmSectionsOrders[(size_t)code];
69 }
70 
lookup(const byte * string,uint32_t length)71 WasmSection::Code WasmSection::lookup(const byte* string, uint32_t length) {
72   // TODO(jfb) Linear search, it may be better to do a common-prefix search.
73   for (Code i = begin(); i != end(); i = next(i)) {
74     if (getNameLength(i) == length && 0 == memcmp(getName(i), string, length)) {
75       return i;
76     }
77   }
78   return Code::Max;
79 }
80 
operator <<(std::ostream & os,const WasmModule & module)81 std::ostream& operator<<(std::ostream& os, const WasmModule& module) {
82   os << "WASM module with ";
83   os << (module.min_mem_pages * module.kPageSize) << " min mem";
84   os << (module.max_mem_pages * module.kPageSize) << " max mem";
85   os << module.functions.size() << " functions";
86   os << module.functions.size() << " globals";
87   os << module.functions.size() << " data segments";
88   return os;
89 }
90 
operator <<(std::ostream & os,const WasmFunction & function)91 std::ostream& operator<<(std::ostream& os, const WasmFunction& function) {
92   os << "WASM function with signature " << *function.sig;
93 
94   os << " code bytes: "
95      << (function.code_end_offset - function.code_start_offset);
96   return os;
97 }
98 
operator <<(std::ostream & os,const WasmFunctionName & pair)99 std::ostream& operator<<(std::ostream& os, const WasmFunctionName& pair) {
100   os << "#" << pair.function_->func_index << ":";
101   if (pair.function_->name_offset > 0) {
102     if (pair.module_) {
103       WasmName name = pair.module_->GetName(pair.function_->name_offset,
104                                             pair.function_->name_length);
105       os.write(name.start(), name.length());
106     } else {
107       os << "+" << pair.function_->func_index;
108     }
109   } else {
110     os << "?";
111   }
112   return os;
113 }
114 
115 namespace {
116 // Internal constants for the layout of the module object.
117 const int kWasmModuleFunctionTable = 0;
118 const int kWasmModuleCodeTable = 1;
119 const int kWasmMemArrayBuffer = 2;
120 const int kWasmGlobalsArrayBuffer = 3;
121 // TODO(clemensh): Remove function name array, extract names from module bytes.
122 const int kWasmFunctionNamesArray = 4;
123 const int kWasmModuleBytesString = 5;
124 const int kWasmDebugInfo = 6;
125 const int kWasmModuleInternalFieldCount = 7;
126 
GetMinModuleMemSize(const WasmModule * module)127 uint32_t GetMinModuleMemSize(const WasmModule* module) {
128   return WasmModule::kPageSize * module->min_mem_pages;
129 }
130 
LoadDataSegments(const WasmModule * module,byte * mem_addr,size_t mem_size)131 void LoadDataSegments(const WasmModule* module, byte* mem_addr,
132                       size_t mem_size) {
133   for (const WasmDataSegment& segment : module->data_segments) {
134     if (!segment.init) continue;
135     if (!segment.source_size) continue;
136     CHECK_LT(segment.dest_addr, mem_size);
137     CHECK_LE(segment.source_size, mem_size);
138     CHECK_LE(segment.dest_addr + segment.source_size, mem_size);
139     byte* addr = mem_addr + segment.dest_addr;
140     memcpy(addr, module->module_start + segment.source_offset,
141            segment.source_size);
142   }
143 }
144 
BuildFunctionTable(Isolate * isolate,const WasmModule * module)145 Handle<FixedArray> BuildFunctionTable(Isolate* isolate,
146                                       const WasmModule* module) {
147   // Compute the size of the indirect function table
148   uint32_t table_size = module->FunctionTableSize();
149   if (table_size == 0) {
150     return Handle<FixedArray>::null();
151   }
152 
153   Handle<FixedArray> fixed = isolate->factory()->NewFixedArray(2 * table_size);
154   for (uint32_t i = 0;
155        i < static_cast<uint32_t>(module->function_table.size());
156        ++i) {
157     const WasmFunction* function =
158         &module->functions[module->function_table[i]];
159     fixed->set(i, Smi::FromInt(function->sig_index));
160   }
161   return fixed;
162 }
163 
NewArrayBuffer(Isolate * isolate,size_t size,byte ** backing_store)164 Handle<JSArrayBuffer> NewArrayBuffer(Isolate* isolate, size_t size,
165                                      byte** backing_store) {
166   *backing_store = nullptr;
167   if (size > (WasmModule::kMaxMemPages * WasmModule::kPageSize)) {
168     // TODO(titzer): lift restriction on maximum memory allocated here.
169     return Handle<JSArrayBuffer>::null();
170   }
171   void* memory = isolate->array_buffer_allocator()->Allocate(size);
172   if (memory == nullptr) {
173     return Handle<JSArrayBuffer>::null();
174   }
175 
176   *backing_store = reinterpret_cast<byte*>(memory);
177 
178 #if DEBUG
179   // Double check the API allocator actually zero-initialized the memory.
180   byte* bytes = reinterpret_cast<byte*>(*backing_store);
181   for (size_t i = 0; i < size; ++i) {
182     DCHECK_EQ(0, bytes[i]);
183   }
184 #endif
185 
186   Handle<JSArrayBuffer> buffer = isolate->factory()->NewJSArrayBuffer();
187   JSArrayBuffer::Setup(buffer, isolate, false, memory, static_cast<int>(size));
188   buffer->set_is_neuterable(false);
189   return buffer;
190 }
191 
RelocateInstanceCode(WasmModuleInstance * instance)192 void RelocateInstanceCode(WasmModuleInstance* instance) {
193   for (uint32_t i = 0; i < instance->function_code.size(); ++i) {
194     Handle<Code> function = instance->function_code[i];
195     AllowDeferredHandleDereference embedding_raw_address;
196     int mask = (1 << RelocInfo::WASM_MEMORY_REFERENCE) |
197                (1 << RelocInfo::WASM_MEMORY_SIZE_REFERENCE);
198     for (RelocIterator it(*function, mask); !it.done(); it.next()) {
199       it.rinfo()->update_wasm_memory_reference(
200           nullptr, instance->mem_start, GetMinModuleMemSize(instance->module),
201           static_cast<uint32_t>(instance->mem_size));
202     }
203   }
204 }
205 
206 // Set the memory for a module instance to be the {memory} array buffer.
SetMemory(WasmModuleInstance * instance,Handle<JSArrayBuffer> memory)207 void SetMemory(WasmModuleInstance* instance, Handle<JSArrayBuffer> memory) {
208   memory->set_is_neuterable(false);
209   instance->mem_start = reinterpret_cast<byte*>(memory->backing_store());
210   instance->mem_size = memory->byte_length()->Number();
211   instance->mem_buffer = memory;
212   RelocateInstanceCode(instance);
213 }
214 
215 // Allocate memory for a module instance as a new JSArrayBuffer.
AllocateMemory(ErrorThrower * thrower,Isolate * isolate,WasmModuleInstance * instance)216 bool AllocateMemory(ErrorThrower* thrower, Isolate* isolate,
217                     WasmModuleInstance* instance) {
218   DCHECK(instance->module);
219   DCHECK(instance->mem_buffer.is_null());
220 
221   if (instance->module->min_mem_pages > WasmModule::kMaxMemPages) {
222     thrower->Error("Out of memory: wasm memory too large");
223     return false;
224   }
225   instance->mem_size = GetMinModuleMemSize(instance->module);
226   instance->mem_buffer =
227       NewArrayBuffer(isolate, instance->mem_size, &instance->mem_start);
228   if (instance->mem_start == nullptr) {
229     thrower->Error("Out of memory: wasm memory");
230     instance->mem_size = 0;
231     return false;
232   }
233   RelocateInstanceCode(instance);
234   return true;
235 }
236 
AllocateGlobals(ErrorThrower * thrower,Isolate * isolate,WasmModuleInstance * instance)237 bool AllocateGlobals(ErrorThrower* thrower, Isolate* isolate,
238                      WasmModuleInstance* instance) {
239   uint32_t globals_size = instance->module->globals_size;
240   if (globals_size > 0) {
241     instance->globals_buffer =
242         NewArrayBuffer(isolate, globals_size, &instance->globals_start);
243     if (!instance->globals_start) {
244       // Not enough space for backing store of globals.
245       thrower->Error("Out of memory: wasm globals");
246       return false;
247     }
248 
249     for (uint32_t i = 0; i < instance->function_code.size(); ++i) {
250       Handle<Code> function = instance->function_code[i];
251       AllowDeferredHandleDereference embedding_raw_address;
252       int mask = 1 << RelocInfo::WASM_GLOBAL_REFERENCE;
253       for (RelocIterator it(*function, mask); !it.done(); it.next()) {
254         it.rinfo()->update_wasm_global_reference(nullptr,
255                                                  instance->globals_start);
256       }
257     }
258   }
259   return true;
260 }
261 
CreatePlaceholder(Factory * factory,uint32_t index,Code::Kind kind)262 Handle<Code> CreatePlaceholder(Factory* factory, uint32_t index,
263                                Code::Kind kind) {
264   // Create a placeholder code object and encode the corresponding index in
265   // the {constant_pool_offset} field of the code object.
266   // TODO(titzer): placeholder code objects are somewhat dangerous.
267   static byte buffer[] = {0, 0, 0, 0, 0, 0, 0, 0};  // fake instructions.
268   static CodeDesc desc = {buffer, 8, 8, 0, 0, nullptr, 0, nullptr};
269   Handle<Code> code = factory->NewCode(desc, Code::KindField::encode(kind),
270                                        Handle<Object>::null());
271   code->set_constant_pool_offset(static_cast<int>(index) + kPlaceholderMarker);
272   return code;
273 }
274 
275 // TODO(mtrofin): remove when we stop relying on placeholders.
InitializePlaceholders(Factory * factory,std::vector<Handle<Code>> * placeholders,size_t size)276 void InitializePlaceholders(Factory* factory,
277                             std::vector<Handle<Code>>* placeholders,
278                             size_t size) {
279   DCHECK(placeholders->empty());
280   placeholders->reserve(size);
281 
282   for (uint32_t i = 0; i < size; ++i) {
283     placeholders->push_back(CreatePlaceholder(factory, i, Code::WASM_FUNCTION));
284   }
285 }
286 
LinkFunction(Handle<Code> unlinked,const std::vector<Handle<Code>> & code_targets,Code::Kind kind)287 bool LinkFunction(Handle<Code> unlinked,
288                   const std::vector<Handle<Code>>& code_targets,
289                   Code::Kind kind) {
290   bool modified = false;
291   int mode_mask = RelocInfo::kCodeTargetMask;
292   AllowDeferredHandleDereference embedding_raw_address;
293   for (RelocIterator it(*unlinked, mode_mask); !it.done(); it.next()) {
294     RelocInfo::Mode mode = it.rinfo()->rmode();
295     if (RelocInfo::IsCodeTarget(mode)) {
296       Code* target =
297           Code::GetCodeFromTargetAddress(it.rinfo()->target_address());
298       if (target->kind() == kind &&
299           target->constant_pool_offset() >= kPlaceholderMarker) {
300         // Patch direct calls to placeholder code objects.
301         uint32_t index = target->constant_pool_offset() - kPlaceholderMarker;
302         CHECK(index < code_targets.size());
303         Handle<Code> new_target = code_targets[index];
304         if (target != *new_target) {
305           it.rinfo()->set_target_address(new_target->instruction_start(),
306                                          SKIP_WRITE_BARRIER, SKIP_ICACHE_FLUSH);
307           modified = true;
308         }
309       }
310     }
311   }
312   return modified;
313 }
314 
LinkModuleFunctions(Isolate * isolate,std::vector<Handle<Code>> & functions)315 void LinkModuleFunctions(Isolate* isolate,
316                          std::vector<Handle<Code>>& functions) {
317   for (size_t i = 0; i < functions.size(); ++i) {
318     Handle<Code> code = functions[i];
319     bool modified = LinkFunction(code, functions, Code::WASM_FUNCTION);
320     if (modified) {
321       Assembler::FlushICache(isolate, code->instruction_start(),
322                              code->instruction_size());
323     }
324   }
325 }
326 
LinkImports(Isolate * isolate,std::vector<Handle<Code>> & functions,const std::vector<Handle<Code>> & imports)327 void LinkImports(Isolate* isolate, std::vector<Handle<Code>>& functions,
328                  const std::vector<Handle<Code>>& imports) {
329   for (uint32_t i = 0; i < functions.size(); ++i) {
330     Handle<Code> code = functions[i];
331     bool modified = LinkFunction(code, imports, Code::WASM_TO_JS_FUNCTION);
332     if (modified) {
333       Assembler::FlushICache(isolate, code->instruction_start(),
334                              code->instruction_size());
335     }
336   }
337 }
338 
339 }  // namespace
340 
WasmModule()341 WasmModule::WasmModule()
342     : module_start(nullptr),
343       module_end(nullptr),
344       min_mem_pages(0),
345       max_mem_pages(0),
346       mem_export(false),
347       mem_external(false),
348       start_function_index(-1),
349       origin(kWasmOrigin),
350       globals_size(0),
351       indirect_table_size(0),
352       pending_tasks(new base::Semaphore(0)) {}
353 
ReportFFIError(ErrorThrower & thrower,const char * error,uint32_t index,wasm::WasmName module_name,wasm::WasmName function_name)354 static MaybeHandle<JSFunction> ReportFFIError(ErrorThrower& thrower,
355                                               const char* error, uint32_t index,
356                                               wasm::WasmName module_name,
357                                               wasm::WasmName function_name) {
358   if (!function_name.is_empty()) {
359     thrower.Error("Import #%d module=\"%.*s\" function=\"%.*s\" error: %s",
360                   index, module_name.length(), module_name.start(),
361                   function_name.length(), function_name.start(), error);
362   } else {
363     thrower.Error("Import #%d module=\"%.*s\" error: %s", index,
364                   module_name.length(), module_name.start(), error);
365   }
366   thrower.Error("Import ");
367   return MaybeHandle<JSFunction>();
368 }
369 
LookupFunction(ErrorThrower & thrower,Factory * factory,Handle<JSReceiver> ffi,uint32_t index,wasm::WasmName module_name,wasm::WasmName function_name)370 static MaybeHandle<JSFunction> LookupFunction(
371     ErrorThrower& thrower, Factory* factory, Handle<JSReceiver> ffi,
372     uint32_t index, wasm::WasmName module_name, wasm::WasmName function_name) {
373   if (ffi.is_null()) {
374     return ReportFFIError(thrower, "FFI is not an object", index, module_name,
375                           function_name);
376   }
377 
378   // Look up the module first.
379   Handle<String> name = factory->InternalizeUtf8String(module_name);
380   MaybeHandle<Object> result = Object::GetProperty(ffi, name);
381   if (result.is_null()) {
382     return ReportFFIError(thrower, "module not found", index, module_name,
383                           function_name);
384   }
385 
386   Handle<Object> module = result.ToHandleChecked();
387 
388   if (!module->IsJSReceiver()) {
389     return ReportFFIError(thrower, "module is not an object or function", index,
390                           module_name, function_name);
391   }
392 
393   Handle<Object> function;
394   if (!function_name.is_empty()) {
395     // Look up the function in the module.
396     Handle<String> name = factory->InternalizeUtf8String(function_name);
397     MaybeHandle<Object> result = Object::GetProperty(module, name);
398     if (result.is_null()) {
399       return ReportFFIError(thrower, "function not found", index, module_name,
400                             function_name);
401     }
402     function = result.ToHandleChecked();
403   } else {
404     // No function specified. Use the "default export".
405     function = module;
406   }
407 
408   if (!function->IsJSFunction()) {
409     return ReportFFIError(thrower, "not a function", index, module_name,
410                           function_name);
411   }
412 
413   return Handle<JSFunction>::cast(function);
414 }
415 
416 namespace {
417 // Fetches the compilation unit of a wasm function and executes its parallel
418 // phase.
FetchAndExecuteCompilationUnit(Isolate * isolate,std::vector<compiler::WasmCompilationUnit * > * compilation_units,std::queue<compiler::WasmCompilationUnit * > * executed_units,base::Mutex * result_mutex,base::AtomicNumber<size_t> * next_unit)419 bool FetchAndExecuteCompilationUnit(
420     Isolate* isolate,
421     std::vector<compiler::WasmCompilationUnit*>* compilation_units,
422     std::queue<compiler::WasmCompilationUnit*>* executed_units,
423     base::Mutex* result_mutex, base::AtomicNumber<size_t>* next_unit) {
424   DisallowHeapAllocation no_allocation;
425   DisallowHandleAllocation no_handles;
426   DisallowHandleDereference no_deref;
427   DisallowCodeDependencyChange no_dependency_change;
428 
429   // - 1 because AtomicIntrement returns the value after the atomic increment.
430   size_t index = next_unit->Increment(1) - 1;
431   if (index >= compilation_units->size()) {
432     return false;
433   }
434 
435   compiler::WasmCompilationUnit* unit = compilation_units->at(index);
436   if (unit != nullptr) {
437     unit->ExecuteCompilation();
438     {
439       base::LockGuard<base::Mutex> guard(result_mutex);
440       executed_units->push(unit);
441     }
442   }
443   return true;
444 }
445 
446 class WasmCompilationTask : public CancelableTask {
447  public:
WasmCompilationTask(Isolate * isolate,std::vector<compiler::WasmCompilationUnit * > * compilation_units,std::queue<compiler::WasmCompilationUnit * > * executed_units,base::Semaphore * on_finished,base::Mutex * result_mutex,base::AtomicNumber<size_t> * next_unit)448   WasmCompilationTask(
449       Isolate* isolate,
450       std::vector<compiler::WasmCompilationUnit*>* compilation_units,
451       std::queue<compiler::WasmCompilationUnit*>* executed_units,
452       base::Semaphore* on_finished, base::Mutex* result_mutex,
453       base::AtomicNumber<size_t>* next_unit)
454       : CancelableTask(isolate),
455         isolate_(isolate),
456         compilation_units_(compilation_units),
457         executed_units_(executed_units),
458         on_finished_(on_finished),
459         result_mutex_(result_mutex),
460         next_unit_(next_unit) {}
461 
RunInternal()462   void RunInternal() override {
463     while (FetchAndExecuteCompilationUnit(isolate_, compilation_units_,
464                                           executed_units_, result_mutex_,
465                                           next_unit_)) {
466     }
467     on_finished_->Signal();
468   }
469 
470   Isolate* isolate_;
471   std::vector<compiler::WasmCompilationUnit*>* compilation_units_;
472   std::queue<compiler::WasmCompilationUnit*>* executed_units_;
473   base::Semaphore* on_finished_;
474   base::Mutex* result_mutex_;
475   base::AtomicNumber<size_t>* next_unit_;
476 };
477 
478 // Records statistics on the code generated by compiling WASM functions.
479 struct CodeStats {
480   size_t code_size;
481   size_t reloc_size;
482 
CodeStatsv8::internal::wasm::__anon590ac46c0211::CodeStats483   inline CodeStats() : code_size(0), reloc_size(0) {}
484 
Recordv8::internal::wasm::__anon590ac46c0211::CodeStats485   inline void Record(Code* code) {
486     code_size += code->body_size();
487     reloc_size += code->relocation_info()->length();
488   }
489 
Reportv8::internal::wasm::__anon590ac46c0211::CodeStats490   inline void Report() {
491     PrintF("Total generated wasm code: %zu bytes\n", code_size);
492     PrintF("Total generated wasm reloc: %zu bytes\n", reloc_size);
493   }
494 };
495 
CompileWrappersToImportedFunctions(Isolate * isolate,const WasmModule * module,const Handle<JSReceiver> ffi,WasmModuleInstance * instance,ErrorThrower * thrower,Factory * factory)496 bool CompileWrappersToImportedFunctions(
497     Isolate* isolate, const WasmModule* module, const Handle<JSReceiver> ffi,
498     WasmModuleInstance* instance, ErrorThrower* thrower, Factory* factory) {
499   if (module->import_table.size() > 0) {
500     instance->import_code.reserve(module->import_table.size());
501     for (uint32_t index = 0; index < module->import_table.size(); ++index) {
502       const WasmImport& import = module->import_table[index];
503       WasmName module_name = module->GetNameOrNull(import.module_name_offset,
504                                                    import.module_name_length);
505       WasmName function_name = module->GetNameOrNull(
506           import.function_name_offset, import.function_name_length);
507       MaybeHandle<JSFunction> function = LookupFunction(
508           *thrower, factory, ffi, index, module_name, function_name);
509       if (function.is_null()) return false;
510 
511       Handle<Code> code = compiler::CompileWasmToJSWrapper(
512           isolate, function.ToHandleChecked(), import.sig, module_name,
513           function_name);
514       instance->import_code[index] = code;
515     }
516   }
517   return true;
518 }
519 
InitializeParallelCompilation(Isolate * isolate,const std::vector<WasmFunction> & functions,std::vector<compiler::WasmCompilationUnit * > & compilation_units,ModuleEnv & module_env,ErrorThrower & thrower)520 void InitializeParallelCompilation(
521     Isolate* isolate, const std::vector<WasmFunction>& functions,
522     std::vector<compiler::WasmCompilationUnit*>& compilation_units,
523     ModuleEnv& module_env, ErrorThrower& thrower) {
524   for (uint32_t i = FLAG_skip_compiling_wasm_funcs; i < functions.size(); ++i) {
525     compilation_units[i] = new compiler::WasmCompilationUnit(
526         &thrower, isolate, &module_env, &functions[i], i);
527   }
528 }
529 
StartCompilationTasks(Isolate * isolate,std::vector<compiler::WasmCompilationUnit * > & compilation_units,std::queue<compiler::WasmCompilationUnit * > & executed_units,base::Semaphore * pending_tasks,base::Mutex & result_mutex,base::AtomicNumber<size_t> & next_unit)530 uint32_t* StartCompilationTasks(
531     Isolate* isolate,
532     std::vector<compiler::WasmCompilationUnit*>& compilation_units,
533     std::queue<compiler::WasmCompilationUnit*>& executed_units,
534     base::Semaphore* pending_tasks, base::Mutex& result_mutex,
535     base::AtomicNumber<size_t>& next_unit) {
536   const size_t num_tasks =
537       Min(static_cast<size_t>(FLAG_wasm_num_compilation_tasks),
538           V8::GetCurrentPlatform()->NumberOfAvailableBackgroundThreads());
539   uint32_t* task_ids = new uint32_t[num_tasks];
540   for (size_t i = 0; i < num_tasks; ++i) {
541     WasmCompilationTask* task =
542         new WasmCompilationTask(isolate, &compilation_units, &executed_units,
543                                 pending_tasks, &result_mutex, &next_unit);
544     task_ids[i] = task->id();
545     V8::GetCurrentPlatform()->CallOnBackgroundThread(
546         task, v8::Platform::kShortRunningTask);
547   }
548   return task_ids;
549 }
550 
WaitForCompilationTasks(Isolate * isolate,uint32_t * task_ids,base::Semaphore * pending_tasks)551 void WaitForCompilationTasks(Isolate* isolate, uint32_t* task_ids,
552                              base::Semaphore* pending_tasks) {
553   const size_t num_tasks =
554       Min(static_cast<size_t>(FLAG_wasm_num_compilation_tasks),
555           V8::GetCurrentPlatform()->NumberOfAvailableBackgroundThreads());
556   for (size_t i = 0; i < num_tasks; ++i) {
557     // If the task has not started yet, then we abort it. Otherwise we wait for
558     // it to finish.
559     if (!isolate->cancelable_task_manager()->TryAbort(task_ids[i])) {
560       pending_tasks->Wait();
561     }
562   }
563 }
564 
FinishCompilationUnits(std::queue<compiler::WasmCompilationUnit * > & executed_units,std::vector<Handle<Code>> & results,base::Mutex & result_mutex)565 void FinishCompilationUnits(
566     std::queue<compiler::WasmCompilationUnit*>& executed_units,
567     std::vector<Handle<Code>>& results, base::Mutex& result_mutex) {
568   while (true) {
569     compiler::WasmCompilationUnit* unit = nullptr;
570     {
571       base::LockGuard<base::Mutex> guard(&result_mutex);
572       if (executed_units.empty()) {
573         break;
574       }
575       unit = executed_units.front();
576       executed_units.pop();
577     }
578     int j = unit->index();
579     results[j] = unit->FinishCompilation();
580     delete unit;
581   }
582 }
583 
CompileInParallel(Isolate * isolate,const WasmModule * module,std::vector<Handle<Code>> & functions,ErrorThrower * thrower,ModuleEnv * module_env)584 void CompileInParallel(Isolate* isolate, const WasmModule* module,
585                        std::vector<Handle<Code>>& functions,
586                        ErrorThrower* thrower, ModuleEnv* module_env) {
587   // Data structures for the parallel compilation.
588   std::vector<compiler::WasmCompilationUnit*> compilation_units(
589       module->functions.size());
590   std::queue<compiler::WasmCompilationUnit*> executed_units;
591 
592   //-----------------------------------------------------------------------
593   // For parallel compilation:
594   // 1) The main thread allocates a compilation unit for each wasm function
595   //    and stores them in the vector {compilation_units}.
596   // 2) The main thread spawns {WasmCompilationTask} instances which run on
597   //    the background threads.
598   // 3.a) The background threads and the main thread pick one compilation
599   //      unit at a time and execute the parallel phase of the compilation
600   //      unit. After finishing the execution of the parallel phase, the
601   //      result is enqueued in {executed_units}.
602   // 3.b) If {executed_units} contains a compilation unit, the main thread
603   //      dequeues it and finishes the compilation.
604   // 4) After the parallel phase of all compilation units has started, the
605   //    main thread waits for all {WasmCompilationTask} instances to finish.
606   // 5) The main thread finishes the compilation.
607 
608   // Turn on the {CanonicalHandleScope} so that the background threads can
609   // use the node cache.
610   CanonicalHandleScope canonical(isolate);
611 
612   // 1) The main thread allocates a compilation unit for each wasm function
613   //    and stores them in the vector {compilation_units}.
614   InitializeParallelCompilation(isolate, module->functions, compilation_units,
615                                 *module_env, *thrower);
616 
617   // Objects for the synchronization with the background threads.
618   base::Mutex result_mutex;
619   base::AtomicNumber<size_t> next_unit(
620       static_cast<size_t>(FLAG_skip_compiling_wasm_funcs));
621 
622   // 2) The main thread spawns {WasmCompilationTask} instances which run on
623   //    the background threads.
624   base::SmartArrayPointer<uint32_t> task_ids(StartCompilationTasks(
625       isolate, compilation_units, executed_units, module->pending_tasks.get(),
626       result_mutex, next_unit));
627 
628   // 3.a) The background threads and the main thread pick one compilation
629   //      unit at a time and execute the parallel phase of the compilation
630   //      unit. After finishing the execution of the parallel phase, the
631   //      result is enqueued in {executed_units}.
632   while (FetchAndExecuteCompilationUnit(isolate, &compilation_units,
633                                         &executed_units, &result_mutex,
634                                         &next_unit)) {
635     // 3.b) If {executed_units} contains a compilation unit, the main thread
636     //      dequeues it and finishes the compilation unit. Compilation units
637     //      are finished concurrently to the background threads to save
638     //      memory.
639     FinishCompilationUnits(executed_units, functions, result_mutex);
640   }
641   // 4) After the parallel phase of all compilation units has started, the
642   //    main thread waits for all {WasmCompilationTask} instances to finish.
643   WaitForCompilationTasks(isolate, task_ids.get(), module->pending_tasks.get());
644   // Finish the compilation of the remaining compilation units.
645   FinishCompilationUnits(executed_units, functions, result_mutex);
646 }
647 
CompileSequentially(Isolate * isolate,const WasmModule * module,std::vector<Handle<Code>> & functions,ErrorThrower * thrower,ModuleEnv * module_env)648 void CompileSequentially(Isolate* isolate, const WasmModule* module,
649                          std::vector<Handle<Code>>& functions,
650                          ErrorThrower* thrower, ModuleEnv* module_env) {
651   DCHECK(!thrower->error());
652 
653   for (uint32_t i = FLAG_skip_compiling_wasm_funcs;
654        i < module->functions.size(); ++i) {
655     const WasmFunction& func = module->functions[i];
656 
657     DCHECK_EQ(i, func.func_index);
658     WasmName str = module->GetName(func.name_offset, func.name_length);
659     Handle<Code> code = Handle<Code>::null();
660     // Compile the function.
661     code = compiler::WasmCompilationUnit::CompileWasmFunction(
662         thrower, isolate, module_env, &func);
663     if (code.is_null()) {
664       thrower->Error("Compilation of #%d:%.*s failed.", i, str.length(),
665                      str.start());
666       break;
667     }
668       // Install the code into the linker table.
669     functions[i] = code;
670   }
671 }
672 
PopulateFunctionTable(WasmModuleInstance * instance)673 void PopulateFunctionTable(WasmModuleInstance* instance) {
674   if (!instance->function_table.is_null()) {
675     uint32_t table_size = instance->module->FunctionTableSize();
676     DCHECK_EQ(table_size * 2, instance->function_table->length());
677     uint32_t populated_table_size =
678         static_cast<uint32_t>(instance->module->function_table.size());
679     for (uint32_t i = 0; i < populated_table_size; ++i) {
680     instance->function_table->set(
681         i + table_size,
682         *instance->function_code[instance->module->function_table[i]]);
683     }
684   }
685 }
686 }  // namespace
687 
SetDeoptimizationData(Factory * factory,Handle<JSObject> js_object,std::vector<Handle<Code>> & functions)688 void SetDeoptimizationData(Factory* factory, Handle<JSObject> js_object,
689                            std::vector<Handle<Code>>& functions) {
690   for (size_t i = FLAG_skip_compiling_wasm_funcs; i < functions.size(); ++i) {
691     Handle<Code> code = functions[i];
692     DCHECK(code->deoptimization_data() == nullptr ||
693            code->deoptimization_data()->length() == 0);
694     Handle<FixedArray> deopt_data = factory->NewFixedArray(2, TENURED);
695     if (!js_object.is_null()) {
696       deopt_data->set(0, *js_object);
697     }
698     deopt_data->set(1, Smi::FromInt(static_cast<int>(i)));
699     deopt_data->set_length(2);
700     code->set_deoptimization_data(*deopt_data);
701   }
702 }
703 
CompileFunctions(Isolate * isolate) const704 Handle<FixedArray> WasmModule::CompileFunctions(Isolate* isolate) const {
705   Factory* factory = isolate->factory();
706   ErrorThrower thrower(isolate, "WasmModule::CompileFunctions()");
707 
708   WasmModuleInstance temp_instance_for_compilation(this);
709   temp_instance_for_compilation.function_table =
710       BuildFunctionTable(isolate, this);
711   temp_instance_for_compilation.context = isolate->native_context();
712   temp_instance_for_compilation.mem_size = GetMinModuleMemSize(this);
713   temp_instance_for_compilation.mem_start = nullptr;
714   temp_instance_for_compilation.globals_start = nullptr;
715 
716   ModuleEnv module_env;
717   module_env.module = this;
718   module_env.instance = &temp_instance_for_compilation;
719   module_env.origin = origin;
720   InitializePlaceholders(factory, &module_env.placeholders, functions.size());
721 
722   Handle<FixedArray> ret =
723       factory->NewFixedArray(static_cast<int>(functions.size()), TENURED);
724 
725   temp_instance_for_compilation.import_code.resize(import_table.size());
726   for (uint32_t i = 0; i < import_table.size(); ++i) {
727     temp_instance_for_compilation.import_code[i] =
728         CreatePlaceholder(factory, i, Code::WASM_TO_JS_FUNCTION);
729   }
730   isolate->counters()->wasm_functions_per_module()->AddSample(
731       static_cast<int>(functions.size()));
732   if (FLAG_wasm_num_compilation_tasks != 0) {
733     CompileInParallel(isolate, this,
734                       temp_instance_for_compilation.function_code, &thrower,
735                       &module_env);
736   } else {
737     CompileSequentially(isolate, this,
738                         temp_instance_for_compilation.function_code, &thrower,
739                         &module_env);
740   }
741   if (thrower.error()) {
742     return Handle<FixedArray>::null();
743   }
744 
745   LinkModuleFunctions(isolate, temp_instance_for_compilation.function_code);
746 
747   // At this point, compilation has completed. Update the code table
748   // and record sizes.
749   for (size_t i = FLAG_skip_compiling_wasm_funcs;
750        i < temp_instance_for_compilation.function_code.size(); ++i) {
751     Code* code = *temp_instance_for_compilation.function_code[i];
752     ret->set(static_cast<int>(i), code);
753   }
754 
755   PopulateFunctionTable(&temp_instance_for_compilation);
756 
757   return ret;
758 }
759 
760 // Instantiates a wasm module as a JSObject.
761 //  * allocates a backing store of {mem_size} bytes.
762 //  * installs a named property "memory" for that buffer if exported
763 //  * installs named properties on the object for exported functions
764 //  * compiles wasm code to machine code
Instantiate(Isolate * isolate,Handle<JSReceiver> ffi,Handle<JSArrayBuffer> memory) const765 MaybeHandle<JSObject> WasmModule::Instantiate(
766     Isolate* isolate, Handle<JSReceiver> ffi,
767     Handle<JSArrayBuffer> memory) const {
768   HistogramTimerScope wasm_instantiate_module_time_scope(
769       isolate->counters()->wasm_instantiate_module_time());
770   ErrorThrower thrower(isolate, "WasmModule::Instantiate()");
771   Factory* factory = isolate->factory();
772 
773   //-------------------------------------------------------------------------
774   // Allocate the instance and its JS counterpart.
775   //-------------------------------------------------------------------------
776   Handle<Map> map = factory->NewMap(
777       JS_OBJECT_TYPE,
778       JSObject::kHeaderSize + kWasmModuleInternalFieldCount * kPointerSize);
779   WasmModuleInstance instance(this);
780   instance.context = isolate->native_context();
781   instance.js_object = factory->NewJSObjectFromMap(map, TENURED);
782 
783   Handle<FixedArray> code_table = CompileFunctions(isolate);
784   if (code_table.is_null()) return Handle<JSObject>::null();
785 
786   instance.js_object->SetInternalField(kWasmModuleCodeTable, *code_table);
787   size_t module_bytes_len =
788       instance.module->module_end - instance.module->module_start;
789   DCHECK_LE(module_bytes_len, static_cast<size_t>(kMaxInt));
790   Vector<const uint8_t> module_bytes_vec(instance.module->module_start,
791                                          static_cast<int>(module_bytes_len));
792   Handle<String> module_bytes_string =
793       factory->NewStringFromOneByte(module_bytes_vec, TENURED)
794           .ToHandleChecked();
795   instance.js_object->SetInternalField(kWasmModuleBytesString,
796                                        *module_bytes_string);
797 
798   for (uint32_t i = 0; i < functions.size(); ++i) {
799     Handle<Code> code = Handle<Code>(Code::cast(code_table->get(i)));
800     instance.function_code[i] = code;
801   }
802 
803   //-------------------------------------------------------------------------
804   // Allocate and initialize the linear memory.
805   //-------------------------------------------------------------------------
806   isolate->counters()->wasm_min_mem_pages_count()->AddSample(
807       instance.module->min_mem_pages);
808   isolate->counters()->wasm_max_mem_pages_count()->AddSample(
809       instance.module->max_mem_pages);
810   if (memory.is_null()) {
811     if (!AllocateMemory(&thrower, isolate, &instance)) {
812       return MaybeHandle<JSObject>();
813     }
814   } else {
815     SetMemory(&instance, memory);
816   }
817   instance.js_object->SetInternalField(kWasmMemArrayBuffer,
818                                        *instance.mem_buffer);
819   LoadDataSegments(this, instance.mem_start, instance.mem_size);
820 
821   //-------------------------------------------------------------------------
822   // Allocate the globals area if necessary.
823   //-------------------------------------------------------------------------
824   if (!AllocateGlobals(&thrower, isolate, &instance)) {
825     return MaybeHandle<JSObject>();
826   }
827   if (!instance.globals_buffer.is_null()) {
828     instance.js_object->SetInternalField(kWasmGlobalsArrayBuffer,
829                                          *instance.globals_buffer);
830   }
831 
832   HistogramTimerScope wasm_compile_module_time_scope(
833       isolate->counters()->wasm_compile_module_time());
834 
835   ModuleEnv module_env;
836   module_env.module = this;
837   module_env.instance = &instance;
838   module_env.origin = origin;
839 
840   //-------------------------------------------------------------------------
841   // Compile wrappers to imported functions.
842   //-------------------------------------------------------------------------
843   if (!CompileWrappersToImportedFunctions(isolate, this, ffi, &instance,
844                                           &thrower, factory)) {
845     return MaybeHandle<JSObject>();
846   }
847 
848   // If FLAG_print_wasm_code_size is set, this aggregates the sum of all code
849   // objects created for this module.
850   // TODO(titzer): switch this to TRACE_EVENT
851   CodeStats code_stats;
852   if (FLAG_print_wasm_code_size) {
853     for (Handle<Code> c : instance.function_code) code_stats.Record(*c);
854     for (Handle<Code> c : instance.import_code) code_stats.Record(*c);
855   }
856 
857   {
858     instance.js_object->SetInternalField(kWasmModuleFunctionTable,
859                                          Smi::FromInt(0));
860     LinkImports(isolate, instance.function_code, instance.import_code);
861 
862     SetDeoptimizationData(factory, instance.js_object, instance.function_code);
863 
864     //-------------------------------------------------------------------------
865     // Create and populate the exports object.
866     //-------------------------------------------------------------------------
867     if (export_table.size() > 0 || mem_export) {
868       Handle<JSObject> exports_object;
869       if (origin == kWasmOrigin) {
870         // Create the "exports" object.
871         Handle<JSFunction> object_function = Handle<JSFunction>(
872             isolate->native_context()->object_function(), isolate);
873         exports_object = factory->NewJSObject(object_function, TENURED);
874         Handle<String> exports_name = factory->InternalizeUtf8String("exports");
875         JSObject::AddProperty(instance.js_object, exports_name, exports_object,
876                               READ_ONLY);
877       } else {
878         // Just export the functions directly on the object returned.
879         exports_object = instance.js_object;
880       }
881 
882       PropertyDescriptor desc;
883       desc.set_writable(false);
884 
885       // Compile wrappers and add them to the exports object.
886       for (const WasmExport& exp : export_table) {
887         if (thrower.error()) break;
888         WasmName str = GetName(exp.name_offset, exp.name_length);
889         Handle<String> name = factory->InternalizeUtf8String(str);
890         Handle<Code> code = instance.function_code[exp.func_index];
891         Handle<JSFunction> function = compiler::CompileJSToWasmWrapper(
892             isolate, &module_env, name, code, instance.js_object,
893             exp.func_index);
894         if (FLAG_print_wasm_code_size) {
895           code_stats.Record(function->code());
896         }
897         desc.set_value(function);
898         Maybe<bool> status = JSReceiver::DefineOwnProperty(
899             isolate, exports_object, name, &desc, Object::THROW_ON_ERROR);
900         if (!status.IsJust()) {
901           thrower.Error("export of %.*s failed.", str.length(), str.start());
902           break;
903         }
904       }
905 
906       if (mem_export) {
907         // Export the memory as a named property.
908         Handle<String> name = factory->InternalizeUtf8String("memory");
909         JSObject::AddProperty(exports_object, name, instance.mem_buffer,
910                               READ_ONLY);
911       }
912     }
913   }
914 
915   if (FLAG_print_wasm_code_size) {
916     code_stats.Report();
917   }
918   //-------------------------------------------------------------------------
919   // Attach the function name table.
920   //-------------------------------------------------------------------------
921   Handle<ByteArray> function_name_table =
922       BuildFunctionNamesTable(isolate, module_env.module);
923   instance.js_object->SetInternalField(kWasmFunctionNamesArray,
924                                        *function_name_table);
925 
926   // Run the start function if one was specified.
927   if (this->start_function_index >= 0) {
928     HandleScope scope(isolate);
929     uint32_t index = static_cast<uint32_t>(this->start_function_index);
930     Handle<String> name = isolate->factory()->NewStringFromStaticChars("start");
931     Handle<Code> code = instance.function_code[index];
932     Handle<JSFunction> jsfunc = compiler::CompileJSToWasmWrapper(
933         isolate, &module_env, name, code, instance.js_object, index);
934 
935     // Call the JS function.
936     Handle<Object> undefined = isolate->factory()->undefined_value();
937     MaybeHandle<Object> retval =
938         Execution::Call(isolate, jsfunc, undefined, 0, nullptr);
939 
940     if (retval.is_null()) {
941       thrower.Error("WASM.instantiateModule(): start function failed");
942     }
943   }
944   return instance.js_object;
945 }
946 
947 // TODO(mtrofin): remove this once we move to WASM_DIRECT_CALL
GetCodeOrPlaceholder(uint32_t index) const948 Handle<Code> ModuleEnv::GetCodeOrPlaceholder(uint32_t index) const {
949   DCHECK(IsValidFunction(index));
950   if (!placeholders.empty()) return placeholders[index];
951   DCHECK_NOT_NULL(instance);
952   return instance->function_code[index];
953 }
954 
GetImportCode(uint32_t index)955 Handle<Code> ModuleEnv::GetImportCode(uint32_t index) {
956   DCHECK(IsValidImport(index));
957   return instance ? instance->import_code[index] : Handle<Code>::null();
958 }
959 
GetCallDescriptor(Zone * zone,uint32_t index)960 compiler::CallDescriptor* ModuleEnv::GetCallDescriptor(Zone* zone,
961                                                        uint32_t index) {
962   DCHECK(IsValidFunction(index));
963   // Always make a direct call to whatever is in the table at that location.
964   // A wrapper will be generated for FFI calls.
965   const WasmFunction* function = &module->functions[index];
966   return GetWasmCallDescriptor(zone, function->sig);
967 }
968 
GetWasmFunctionNameOrNull(Isolate * isolate,Handle<Object> wasm,uint32_t func_index)969 Handle<Object> GetWasmFunctionNameOrNull(Isolate* isolate, Handle<Object> wasm,
970                                          uint32_t func_index) {
971   if (!wasm->IsUndefined(isolate)) {
972     Handle<ByteArray> func_names_arr_obj(
973         ByteArray::cast(Handle<JSObject>::cast(wasm)->GetInternalField(
974             kWasmFunctionNamesArray)),
975         isolate);
976     // TODO(clemens): Extract this from the module bytes; skip whole function
977     // name table.
978     Handle<Object> name;
979     if (GetWasmFunctionNameFromTable(func_names_arr_obj, func_index)
980             .ToHandle(&name)) {
981       return name;
982     }
983   }
984   return isolate->factory()->null_value();
985 }
986 
GetWasmFunctionName(Isolate * isolate,Handle<Object> wasm,uint32_t func_index)987 Handle<String> GetWasmFunctionName(Isolate* isolate, Handle<Object> wasm,
988                                    uint32_t func_index) {
989   Handle<Object> name_or_null =
990       GetWasmFunctionNameOrNull(isolate, wasm, func_index);
991   if (!name_or_null->IsNull(isolate)) {
992     return Handle<String>::cast(name_or_null);
993   }
994   return isolate->factory()->NewStringFromStaticChars("<WASM UNNAMED>");
995 }
996 
IsWasmObject(Object * object)997 bool IsWasmObject(Object* object) {
998   if (!object->IsJSObject()) return false;
999   JSObject* obj = JSObject::cast(object);
1000   if (obj->GetInternalFieldCount() != kWasmModuleInternalFieldCount ||
1001       !obj->GetInternalField(kWasmModuleCodeTable)->IsFixedArray() ||
1002       !obj->GetInternalField(kWasmMemArrayBuffer)->IsJSArrayBuffer() ||
1003       !obj->GetInternalField(kWasmFunctionNamesArray)->IsByteArray() ||
1004       !obj->GetInternalField(kWasmModuleBytesString)->IsSeqOneByteString()) {
1005     return false;
1006   }
1007   DisallowHeapAllocation no_gc;
1008   SeqOneByteString* bytes =
1009       SeqOneByteString::cast(obj->GetInternalField(kWasmModuleBytesString));
1010   if (bytes->length() < 4) return false;
1011   if (memcmp(bytes->GetChars(), "\0asm", 4)) return false;
1012 
1013   // All checks passed.
1014   return true;
1015 }
1016 
GetWasmBytes(JSObject * wasm)1017 SeqOneByteString* GetWasmBytes(JSObject* wasm) {
1018   return SeqOneByteString::cast(wasm->GetInternalField(kWasmModuleBytesString));
1019 }
1020 
GetDebugInfo(JSObject * wasm)1021 WasmDebugInfo* GetDebugInfo(JSObject* wasm) {
1022   Object* info = wasm->GetInternalField(kWasmDebugInfo);
1023   if (!info->IsUndefined(wasm->GetIsolate())) return WasmDebugInfo::cast(info);
1024   Handle<WasmDebugInfo> new_info = WasmDebugInfo::New(handle(wasm));
1025   wasm->SetInternalField(kWasmDebugInfo, *new_info);
1026   return *new_info;
1027 }
1028 
1029 namespace testing {
1030 
CompileAndRunWasmModule(Isolate * isolate,const byte * module_start,const byte * module_end,bool asm_js)1031 int32_t CompileAndRunWasmModule(Isolate* isolate, const byte* module_start,
1032                                 const byte* module_end, bool asm_js) {
1033   HandleScope scope(isolate);
1034   Zone zone(isolate->allocator());
1035   ErrorThrower thrower(isolate, "CompileAndRunWasmModule");
1036 
1037   // Decode the module, but don't verify function bodies, since we'll
1038   // be compiling them anyway.
1039   ModuleResult decoding_result =
1040       DecodeWasmModule(isolate, &zone, module_start, module_end, false,
1041                        asm_js ? kAsmJsOrigin : kWasmOrigin);
1042 
1043   std::unique_ptr<const WasmModule> module(decoding_result.val);
1044   if (decoding_result.failed()) {
1045     // Module verification failed. throw.
1046     thrower.Error("WASM.compileRun() failed: %s",
1047                   decoding_result.error_msg.get());
1048     return -1;
1049   }
1050 
1051   if (module->import_table.size() > 0) {
1052     thrower.Error("Not supported: module has imports.");
1053   }
1054   if (module->export_table.size() == 0) {
1055     thrower.Error("Not supported: module has no exports.");
1056   }
1057 
1058   if (thrower.error()) return -1;
1059 
1060   Handle<JSObject> instance =
1061       module
1062           ->Instantiate(isolate, Handle<JSReceiver>::null(),
1063                         Handle<JSArrayBuffer>::null())
1064           .ToHandleChecked();
1065 
1066   Handle<Name> exports = isolate->factory()->InternalizeUtf8String("exports");
1067   Handle<JSObject> exports_object = Handle<JSObject>::cast(
1068       JSObject::GetProperty(instance, exports).ToHandleChecked());
1069   Handle<Name> main_name = isolate->factory()->NewStringFromStaticChars("main");
1070   PropertyDescriptor desc;
1071   Maybe<bool> property_found = JSReceiver::GetOwnPropertyDescriptor(
1072       isolate, exports_object, main_name, &desc);
1073   if (!property_found.FromMaybe(false)) return -1;
1074 
1075   Handle<JSFunction> main_export = Handle<JSFunction>::cast(desc.value());
1076 
1077   // Call the JS function.
1078   Handle<Object> undefined = isolate->factory()->undefined_value();
1079   MaybeHandle<Object> retval =
1080       Execution::Call(isolate, main_export, undefined, 0, nullptr);
1081 
1082   // The result should be a number.
1083   if (retval.is_null()) {
1084     thrower.Error("WASM.compileRun() failed: Invocation was null");
1085     return -1;
1086   }
1087   Handle<Object> result = retval.ToHandleChecked();
1088   if (result->IsSmi()) {
1089     return Smi::cast(*result)->value();
1090   }
1091   if (result->IsHeapNumber()) {
1092     return static_cast<int32_t>(HeapNumber::cast(*result)->value());
1093   }
1094   thrower.Error("WASM.compileRun() failed: Return value should be number");
1095   return -1;
1096 }
1097 
1098 }  // namespace testing
1099 }  // namespace wasm
1100 }  // namespace internal
1101 }  // namespace v8
1102