5 #include "src/asmjs/asm-js.h" 7 #include "src/asmjs/asm-names.h" 8 #include "src/asmjs/asm-parser.h" 9 #include "src/assert-scope.h" 10 #include "src/ast/ast.h" 11 #include "src/base/optional.h" 12 #include "src/base/platform/elapsed-timer.h" 13 #include "src/compiler.h" 14 #include "src/counters.h" 15 #include "src/execution.h" 16 #include "src/handles.h" 17 #include "src/heap/factory.h" 18 #include "src/isolate.h" 19 #include "src/message-template.h" 20 #include "src/objects-inl.h" 21 #include "src/parsing/parse-info.h" 22 #include "src/parsing/scanner-character-streams.h" 23 #include "src/parsing/scanner.h" 24 #include "src/unoptimized-compilation-info.h" 26 #include "src/wasm/wasm-engine.h" 27 #include "src/wasm/wasm-js.h" 28 #include "src/wasm/wasm-limits.h" 29 #include "src/wasm/wasm-module-builder.h" 30 #include "src/wasm/wasm-objects-inl.h" 31 #include "src/wasm/wasm-result.h" 36 const char*
const AsmJs::kSingleFunctionName =
"__single_function__";
40 Handle<Object> StdlibMathMember(Isolate* isolate, Handle<JSReceiver> stdlib,
42 Handle<Name> math_name(
43 isolate->factory()->InternalizeOneByteString(STATIC_CHAR_VECTOR(
"Math")));
44 Handle<Object> math = JSReceiver::GetDataProperty(stdlib, math_name);
45 if (!math->IsJSReceiver())
return isolate->factory()->undefined_value();
46 Handle<JSReceiver> math_receiver = Handle<JSReceiver>::cast(math);
47 Handle<Object> value = JSReceiver::GetDataProperty(math_receiver, name);
51 bool AreStdlibMembersValid(Isolate* isolate, Handle<JSReceiver> stdlib,
52 wasm::AsmJsParser::StdlibSet members,
53 bool* is_typed_array) {
54 if (members.Contains(wasm::AsmJsParser::StandardMember::kInfinity)) {
55 members.Remove(wasm::AsmJsParser::StandardMember::kInfinity);
56 Handle<Name> name = isolate->factory()->Infinity_string();
57 Handle<Object> value = JSReceiver::GetDataProperty(stdlib, name);
58 if (!value->IsNumber() || !std::isinf(value->Number()))
return false;
60 if (members.Contains(wasm::AsmJsParser::StandardMember::kNaN)) {
61 members.Remove(wasm::AsmJsParser::StandardMember::kNaN);
62 Handle<Name> name = isolate->factory()->NaN_string();
63 Handle<Object> value = JSReceiver::GetDataProperty(stdlib, name);
64 if (!value->IsNaN())
return false;
66 #define STDLIB_MATH_FUNC(fname, FName, ignore1, ignore2) \ 67 if (members.Contains(wasm::AsmJsParser::StandardMember::kMath##FName)) { \ 68 members.Remove(wasm::AsmJsParser::StandardMember::kMath##FName); \ 69 Handle<Name> name(isolate->factory()->InternalizeOneByteString( \ 70 STATIC_CHAR_VECTOR(#fname))); \ 71 Handle<Object> value = StdlibMathMember(isolate, stdlib, name); \ 72 if (!value->IsJSFunction()) return false; \ 73 SharedFunctionInfo* shared = Handle<JSFunction>::cast(value)->shared(); \ 74 if (!shared->HasBuiltinId() || \ 75 shared->builtin_id() != Builtins::kMath##FName) { \ 78 DCHECK_EQ(shared->GetCode(), \ 79 isolate->builtins()->builtin(Builtins::kMath##FName)); \ 81 STDLIB_MATH_FUNCTION_LIST(STDLIB_MATH_FUNC)
82 #undef STDLIB_MATH_FUNC 83 #define STDLIB_MATH_CONST(cname, const_value) \ 84 if (members.Contains(wasm::AsmJsParser::StandardMember::kMath##cname)) { \ 85 members.Remove(wasm::AsmJsParser::StandardMember::kMath##cname); \ 86 Handle<Name> name(isolate->factory()->InternalizeOneByteString( \ 87 STATIC_CHAR_VECTOR(#cname))); \ 88 Handle<Object> value = StdlibMathMember(isolate, stdlib, name); \ 89 if (!value->IsNumber() || value->Number() != const_value) return false; \ 91 STDLIB_MATH_VALUE_LIST(STDLIB_MATH_CONST)
92 #undef STDLIB_MATH_CONST 93 #define STDLIB_ARRAY_TYPE(fname, FName) \ 94 if (members.Contains(wasm::AsmJsParser::StandardMember::k##FName)) { \ 95 members.Remove(wasm::AsmJsParser::StandardMember::k##FName); \ 96 *is_typed_array = true; \ 97 Handle<Name> name(isolate->factory()->InternalizeOneByteString( \ 98 STATIC_CHAR_VECTOR(#FName))); \ 99 Handle<Object> value = JSReceiver::GetDataProperty(stdlib, name); \ 100 if (!value->IsJSFunction()) return false; \ 101 Handle<JSFunction> func = Handle<JSFunction>::cast(value); \ 102 if (!func.is_identical_to(isolate->fname())) return false; \ 104 STDLIB_ARRAY_TYPE(int8_array_fun, Int8Array)
105 STDLIB_ARRAY_TYPE(uint8_array_fun, Uint8Array)
106 STDLIB_ARRAY_TYPE(int16_array_fun, Int16Array)
107 STDLIB_ARRAY_TYPE(uint16_array_fun, Uint16Array)
108 STDLIB_ARRAY_TYPE(int32_array_fun, Int32Array)
109 STDLIB_ARRAY_TYPE(uint32_array_fun, Uint32Array)
110 STDLIB_ARRAY_TYPE(float32_array_fun, Float32Array)
111 STDLIB_ARRAY_TYPE(float64_array_fun, Float64Array)
112 #undef STDLIB_ARRAY_TYPE 114 DCHECK(members.IsEmpty());
118 void Report(Handle<Script> script,
int position, Vector<const char> text,
119 MessageTemplate message_template,
120 v8::Isolate::MessageErrorLevel level) {
121 Isolate* isolate = script->GetIsolate();
122 MessageLocation location(script, position, position);
123 Handle<String> text_object = isolate->factory()->InternalizeUtf8String(text);
124 Handle<JSMessageObject> message = MessageHandler::MakeMessageObject(
125 isolate, message_template, &location, text_object,
126 Handle<FixedArray>::null());
127 message->set_error_level(level);
128 MessageHandler::ReportMessage(isolate, &location, message);
132 void ReportCompilationSuccess(Handle<Script> script,
int position,
133 double translate_time,
double compile_time,
134 size_t module_size) {
135 if (FLAG_suppress_asm_messages || !FLAG_trace_asm_time)
return;
136 EmbeddedVector<char, 100> text;
137 int length = SNPrintF(
138 text,
"success, asm->wasm: %0.3f ms, compile: %0.3f ms, %" PRIuS
" bytes",
139 translate_time, compile_time, module_size);
140 CHECK_NE(-1, length);
141 text.Truncate(length);
142 Report(script, position, text, MessageTemplate::kAsmJsCompiled,
143 v8::Isolate::kMessageInfo);
147 void ReportCompilationFailure(ParseInfo* parse_info,
int position,
148 const char* reason) {
149 if (FLAG_suppress_asm_messages)
return;
150 parse_info->pending_error_handler()->ReportWarningAt(
151 position, position, MessageTemplate::kAsmJsInvalid, reason);
155 void ReportInstantiationSuccess(Handle<Script> script,
int position,
156 double instantiate_time) {
157 if (FLAG_suppress_asm_messages || !FLAG_trace_asm_time)
return;
158 EmbeddedVector<char, 50> text;
159 int length = SNPrintF(text,
"success, %0.3f ms", instantiate_time);
160 CHECK_NE(-1, length);
161 text.Truncate(length);
162 Report(script, position, text, MessageTemplate::kAsmJsInstantiated,
163 v8::Isolate::kMessageInfo);
167 void ReportInstantiationFailure(Handle<Script> script,
int position,
168 const char* reason) {
169 if (FLAG_suppress_asm_messages)
return;
170 Vector<const char> text = CStrVector(reason);
171 Report(script, position, text, MessageTemplate::kAsmJsLinkingFailed,
172 v8::Isolate::kMessageWarning);
191 allocator_(allocator),
192 zone_(allocator, ZONE_NAME),
193 compilation_info_(&zone_, parse_info, literal),
195 asm_offsets_(
nullptr),
198 module_source_size_(0),
199 translate_time_micro_(0),
200 translate_zone_size_(0) {}
203 Status ExecuteJobImpl()
final;
208 void RecordHistograms(
Isolate* isolate);
217 double translate_time_;
218 double compile_time_;
219 int module_source_size_;
221 size_t translate_zone_size_;
226 UnoptimizedCompilationJob::Status AsmJsCompilationJob::ExecuteJobImpl() {
228 size_t compile_zone_start = compilation_info()->zone()->allocation_size();
230 translate_timer.Start();
232 Zone* compile_zone = compilation_info()->zone();
233 Zone translate_zone(allocator_, ZONE_NAME);
237 if (stream->can_access_heap()) {
238 allow_deref.emplace();
240 stream->Seek(compilation_info()->literal()->start_position());
241 wasm::AsmJsParser parser(&translate_zone, stack_limit(), stream);
243 if (!FLAG_suppress_asm_messages) {
244 ReportCompilationFailure(parse_info(), parser.failure_location(),
245 parser.failure_message());
249 module_ =
new (compile_zone) wasm::ZoneBuffer(compile_zone);
250 parser.module_builder()->WriteTo(*module_);
251 asm_offsets_ =
new (compile_zone) wasm::ZoneBuffer(compile_zone);
252 parser.module_builder()->WriteAsmJsOffsetTable(*asm_offsets_);
253 stdlib_uses_ = *parser.stdlib_uses();
255 size_t compile_zone_size =
256 compilation_info()->zone()->allocation_size() - compile_zone_start;
257 translate_zone_size_ = translate_zone.allocation_size();
258 translate_time_ = translate_timer.Elapsed().InMillisecondsF();
259 translate_time_micro_ = translate_timer.Elapsed().InMicroseconds();
260 module_source_size_ = compilation_info()->literal()->end_position() -
261 compilation_info()->literal()->start_position();
262 if (FLAG_trace_asm_parser) {
264 "[asm.js translation successful: time=%0.3fms, " 265 "translate_zone=%" PRIuS
"KB, compile_zone+=%" PRIuS
"KB]\n",
266 translate_time_, translate_zone_size_ / KB, compile_zone_size / KB);
271 UnoptimizedCompilationJob::Status AsmJsCompilationJob::FinalizeJobImpl(
272 Handle<SharedFunctionInfo> shared_info, Isolate* isolate) {
274 base::ElapsedTimer compile_timer;
275 compile_timer.Start();
277 Handle<HeapNumber> uses_bitset =
278 isolate->factory()->NewHeapNumberFromBits(stdlib_uses_.ToIntegral());
281 wasm::ErrorThrower thrower(isolate,
"AsmJs::Compile");
282 Handle<AsmWasmData> result =
283 isolate->wasm_engine()
284 ->SyncCompileTranslatedAsmJs(
286 wasm::ModuleWireBytes(module_->begin(), module_->end()),
287 Vector<const byte>(asm_offsets_->begin(), asm_offsets_->size()),
290 DCHECK(!thrower.error());
291 compile_time_ = compile_timer.Elapsed().InMillisecondsF();
293 compilation_info()->SetAsmWasmData(result);
295 RecordHistograms(isolate);
296 ReportCompilationSuccess(parse_info()->script(),
297 compilation_info()->literal()->position(),
298 translate_time_, compile_time_, module_->size());
302 void AsmJsCompilationJob::RecordHistograms(Isolate* isolate) {
303 Counters* counters = isolate->counters();
304 counters->asm_wasm_translation_time()->AddSample(
305 static_cast<int>(translate_time_micro_));
306 counters->asm_wasm_translation_peak_memory_bytes()->AddSample(
307 static_cast<int>(translate_zone_size_));
308 counters->asm_module_size_bytes()->AddSample(module_source_size_);
311 int translation_throughput =
312 translate_time_micro_ != 0
313 ?
static_cast<int>(
static_cast<int64_t>(module_source_size_) /
314 translate_time_micro_)
316 counters->asm_wasm_translation_throughput()->AddSample(
317 translation_throughput);
320 UnoptimizedCompilationJob* AsmJs::NewCompilationJob(
321 ParseInfo* parse_info, FunctionLiteral* literal,
322 AccountingAllocator* allocator) {
323 return new AsmJsCompilationJob(parse_info, literal, allocator);
327 inline bool IsValidAsmjsMemorySize(
size_t size) {
329 if (size < (1u << 12u))
return false;
331 if (size > wasm::max_mem_pages() * uint64_t{wasm::kWasmPageSize}) {
335 if (size < (1u << 24u)) {
337 return base::bits::IsPowerOfTwo(size32);
340 if ((size % (1u << 24u)) != 0)
return false;
346 MaybeHandle<Object> AsmJs::InstantiateAsmWasm(Isolate* isolate,
347 Handle<SharedFunctionInfo> shared,
348 Handle<AsmWasmData> wasm_data,
349 Handle<JSReceiver> stdlib,
350 Handle<JSReceiver> foreign,
351 Handle<JSArrayBuffer> memory) {
352 base::ElapsedTimer instantiate_timer;
353 instantiate_timer.Start();
354 Handle<HeapNumber> uses_bitset(wasm_data->uses_bitset(), isolate);
355 Handle<Script> script(Script::cast(shared->script()), isolate);
358 Handle<WasmModuleObject> module =
359 isolate->wasm_engine()->FinalizeTranslatedAsmJs(isolate, wasm_data,
364 int position = shared->StartPosition();
367 bool stdlib_use_of_typed_array_present =
false;
368 wasm::AsmJsParser::StdlibSet stdlib_uses(uses_bitset->value_as_bits());
369 if (!stdlib_uses.IsEmpty()) {
370 if (stdlib.is_null()) {
371 ReportInstantiationFailure(script, position,
"Requires standard library");
372 return MaybeHandle<Object>();
374 if (!AreStdlibMembersValid(isolate, stdlib, stdlib_uses,
375 &stdlib_use_of_typed_array_present)) {
376 ReportInstantiationFailure(script, position,
"Unexpected stdlib member");
377 return MaybeHandle<Object>();
382 if (stdlib_use_of_typed_array_present) {
383 if (memory.is_null()) {
384 ReportInstantiationFailure(script, position,
"Requires heap buffer");
385 return MaybeHandle<Object>();
387 memory->set_is_growable(
false);
388 size_t size = memory->byte_length();
390 if (!IsValidAsmjsMemorySize(size)) {
391 ReportInstantiationFailure(script, position,
"Invalid heap size");
392 return MaybeHandle<Object>();
395 memory = Handle<JSArrayBuffer>::null();
398 wasm::ErrorThrower thrower(isolate,
"AsmJs::Instantiate");
399 MaybeHandle<Object> maybe_module_object =
400 isolate->wasm_engine()->SyncInstantiate(isolate, &thrower, module,
402 if (maybe_module_object.is_null()) {
405 if (isolate->has_pending_exception()) isolate->clear_pending_exception();
406 if (thrower.error()) {
407 ScopedVector<char> error_reason(100);
408 SNPrintF(error_reason,
"Internal wasm failure: %s", thrower.error_msg());
409 ReportInstantiationFailure(script, position, error_reason.start());
411 ReportInstantiationFailure(script, position,
"Internal wasm failure");
414 return MaybeHandle<Object>();
416 DCHECK(!thrower.error());
417 Handle<Object> module_object = maybe_module_object.ToHandleChecked();
419 ReportInstantiationSuccess(script, position,
420 instantiate_timer.Elapsed().InMillisecondsF());
422 Handle<Name> single_function_name(
423 isolate->factory()->InternalizeUtf8String(AsmJs::kSingleFunctionName));
424 MaybeHandle<Object> single_function =
425 Object::GetProperty(isolate, module_object, single_function_name);
426 if (!single_function.is_null() &&
427 !single_function.ToHandleChecked()->IsUndefined(isolate)) {
428 return single_function;
431 Handle<String> exports_name =
432 isolate->factory()->InternalizeUtf8String(
"exports");
433 return Object::GetProperty(isolate, module_object, exports_name);