v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
asm-js.cc
Go to the documentation of this file.
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/asmjs/asm-js.h"
6
7#include <optional>
8
11#include "src/ast/ast.h"
13#include "src/base/vector.h"
20#include "src/handles/handles.h"
21#include "src/heap/factory.h"
27#include "src/parsing/scanner.h"
29#include "src/wasm/wasm-js.h"
34
35namespace v8 {
36namespace internal {
37
38const char* const AsmJs::kSingleFunctionName = "__single_function__";
39
40namespace {
41
42DirectHandle<Object> StdlibMathMember(Isolate* isolate,
43 DirectHandle<JSReceiver> stdlib,
44 DirectHandle<Name> name) {
45 DirectHandle<Name> math_name(
46 isolate->factory()->InternalizeString(base::StaticCharVector("Math")));
47 DirectHandle<Object> math =
48 JSReceiver::GetDataProperty(isolate, stdlib, math_name);
49 if (!IsJSReceiver(*math)) return isolate->factory()->undefined_value();
50 DirectHandle<JSReceiver> math_receiver = Cast<JSReceiver>(math);
51 return JSReceiver::GetDataProperty(isolate, math_receiver, name);
52}
53
54bool AreStdlibMembersValid(Isolate* isolate, DirectHandle<JSReceiver> stdlib,
56 bool* is_typed_array) {
59 DirectHandle<Name> name = isolate->factory()->Infinity_string();
60 DirectHandle<Object> value =
61 JSReceiver::GetDataProperty(isolate, stdlib, name);
62 if (!IsNumber(*value) || !std::isinf(Object::NumberValue(*value)))
63 return false;
64 }
65 if (members.contains(wasm::AsmJsParser::StandardMember::kNaN)) {
67 DirectHandle<Name> name = isolate->factory()->NaN_string();
68 DirectHandle<Object> value =
69 JSReceiver::GetDataProperty(isolate, stdlib, name);
70 if (!IsNaN(*value)) return false;
71 }
72#define STDLIB_MATH_FUNC(fname, FName, ignore1, ignore2) \
73 if (members.contains(wasm::AsmJsParser::StandardMember::kMath##FName)) { \
74 members.Remove(wasm::AsmJsParser::StandardMember::kMath##FName); \
75 DirectHandle<Name> name(isolate->factory()->InternalizeString( \
76 base::StaticCharVector(#fname))); \
77 DirectHandle<Object> value = StdlibMathMember(isolate, stdlib, name); \
78 if (!IsJSFunction(*value)) return false; \
79 Tagged<SharedFunctionInfo> shared = Cast<JSFunction>(value)->shared(); \
80 if (!shared->HasBuiltinId() || \
81 shared->builtin_id() != Builtin::kMath##FName) { \
82 return false; \
83 } \
84 DCHECK_EQ(shared->GetCode(isolate), \
85 isolate->builtins()->code(Builtin::kMath##FName)); \
86 }
88#undef STDLIB_MATH_FUNC
89#define STDLIB_MATH_CONST(cname, const_value) \
90 if (members.contains(wasm::AsmJsParser::StandardMember::kMath##cname)) { \
91 members.Remove(wasm::AsmJsParser::StandardMember::kMath##cname); \
92 DirectHandle<Name> name(isolate->factory()->InternalizeString( \
93 base::StaticCharVector(#cname))); \
94 DirectHandle<Object> value = StdlibMathMember(isolate, stdlib, name); \
95 if (!IsNumber(*value) || Object::NumberValue(*value) != const_value) \
96 return false; \
97 }
99#undef STDLIB_MATH_CONST
100#define STDLIB_ARRAY_TYPE(fname, FName) \
101 if (members.contains(wasm::AsmJsParser::StandardMember::k##FName)) { \
102 members.Remove(wasm::AsmJsParser::StandardMember::k##FName); \
103 *is_typed_array = true; \
104 DirectHandle<Name> name(isolate->factory()->InternalizeString( \
105 base::StaticCharVector(#FName))); \
106 DirectHandle<Object> value = \
107 JSReceiver::GetDataProperty(isolate, stdlib, name); \
108 if (!IsJSFunction(*value)) return false; \
109 DirectHandle<JSFunction> func = Cast<JSFunction>(value); \
110 if (!func.is_identical_to(isolate->fname())) return false; \
111 }
112 STDLIB_ARRAY_TYPE(int8_array_fun, Int8Array)
113 STDLIB_ARRAY_TYPE(uint8_array_fun, Uint8Array)
114 STDLIB_ARRAY_TYPE(int16_array_fun, Int16Array)
115 STDLIB_ARRAY_TYPE(uint16_array_fun, Uint16Array)
116 STDLIB_ARRAY_TYPE(int32_array_fun, Int32Array)
117 STDLIB_ARRAY_TYPE(uint32_array_fun, Uint32Array)
118 STDLIB_ARRAY_TYPE(float32_array_fun, Float32Array)
119 STDLIB_ARRAY_TYPE(float64_array_fun, Float64Array)
120#undef STDLIB_ARRAY_TYPE
121 // All members accounted for.
122 DCHECK(members.empty());
123 return true;
124}
125
126void Report(Handle<Script> script, int position, base::Vector<const char> text,
127 MessageTemplate message_template,
129 Isolate* isolate = script->GetIsolate();
130 MessageLocation location(script, position, position);
131 DirectHandle<String> text_object =
132 isolate->factory()->InternalizeUtf8String(text);
133 DirectHandle<JSMessageObject> message = MessageHandler::MakeMessageObject(
134 isolate, message_template, &location, text_object);
135 message->set_error_level(level);
136 MessageHandler::ReportMessage(isolate, &location, message);
137}
138
139// Hook to report successful execution of {AsmJs::CompileAsmViaWasm} phase.
140void ReportCompilationSuccess(Handle<Script> script, int position,
141 double compile_time, size_t module_size) {
142 if (v8_flags.suppress_asm_messages || !v8_flags.trace_asm_time) return;
143 base::EmbeddedVector<char, 100> text;
144 int length = SNPrintF(text, "success, compile time %0.3f ms, %zu bytes",
145 compile_time, module_size);
146 CHECK_NE(-1, length);
147 text.Truncate(length);
148 Report(script, position, text, MessageTemplate::kAsmJsCompiled,
150}
151
152// Hook to report failed execution of {AsmJs::CompileAsmViaWasm} phase.
153void ReportCompilationFailure(ParseInfo* parse_info, int position,
154 const char* reason) {
155 if (v8_flags.suppress_asm_messages) return;
156 parse_info->pending_error_handler()->ReportWarningAt(
157 position, position, MessageTemplate::kAsmJsInvalid, reason);
158}
159
160// Hook to report successful execution of {AsmJs::InstantiateAsmWasm} phase.
161void ReportInstantiationSuccess(Handle<Script> script, int position,
162 double instantiate_time) {
163 if (v8_flags.suppress_asm_messages || !v8_flags.trace_asm_time) return;
164 base::EmbeddedVector<char, 50> text;
165 int length = SNPrintF(text, "success, %0.3f ms", instantiate_time);
166 CHECK_NE(-1, length);
167 text.Truncate(length);
168 Report(script, position, text, MessageTemplate::kAsmJsInstantiated,
170}
171
172// Hook to report failed execution of {AsmJs::InstantiateAsmWasm} phase.
173void ReportInstantiationFailure(Handle<Script> script, int position,
174 const char* reason) {
175 if (v8_flags.suppress_asm_messages) return;
176 base::Vector<const char> text = base::CStrVector(reason);
177 Report(script, position, text, MessageTemplate::kAsmJsLinkingFailed,
179}
180
181} // namespace
182
183// The compilation of asm.js modules is split into two distinct steps:
184// [1] ExecuteJobImpl: The asm.js module source is parsed, validated, and
185// translated to a valid WebAssembly module. The result are two vectors
186// representing the encoded module as well as encoded source position
187// information and a StdlibSet bit set.
188// [2] FinalizeJobImpl: The module is handed to WebAssembly which decodes it
189// into an internal representation and eventually compiles it to machine
190// code.
230
232 DisallowHeapAccess no_heap_access;
233
234 // Step 1: Translate asm.js module to WebAssembly module.
235 Zone* compile_zone = &zone_;
236 Zone translate_zone(allocator_, ZONE_NAME);
237
239 std::optional<AllowHandleDereference> allow_deref;
240 if (stream->can_access_heap()) {
241 allow_deref.emplace();
242 }
243 stream->Seek(compilation_info()->literal()->start_position());
244 wasm::AsmJsParser parser(&translate_zone, stack_limit(), stream);
245 if (!parser.Run()) {
246 if (!v8_flags.suppress_asm_messages) {
247 ReportCompilationFailure(parse_info(), parser.failure_location(),
248 parser.failure_message());
249 }
250 return FAILED;
251 }
252 module_ = compile_zone->New<wasm::ZoneBuffer>(compile_zone);
253 parser.module_builder()->WriteTo(module_);
254 if (module_->size() > v8_flags.wasm_max_module_size) {
255 if (!v8_flags.suppress_asm_messages) {
256 ReportCompilationFailure(
257 parse_info(), parser.failure_location(),
258 "Module size exceeds engine's supported maximum");
259 }
260 return FAILED;
261 }
262 asm_offsets_ = compile_zone->New<wasm::ZoneBuffer>(compile_zone);
264 stdlib_uses_ = *parser.stdlib_uses();
265
268 return SUCCEEDED;
269}
270
272 DirectHandle<SharedFunctionInfo> shared_info, Isolate* isolate) {
273 // Step 2: Compile and decode the WebAssembly module.
274 base::ElapsedTimer compile_timer;
275 compile_timer.Start();
276
277 DirectHandle<HeapNumber> uses_bitset =
278 isolate->factory()->NewHeapNumberFromBits(stdlib_uses_.ToIntegral());
279
280 // The result is a compiled module and serialized standard library uses.
281 wasm::ErrorThrower thrower(isolate, "AsmJs::Compile");
282 Handle<Script> script(Cast<Script>(shared_info->script()), isolate);
286 isolate, &thrower, base::OwnedCopyOf(*module_), script,
287 base::VectorOf(*asm_offsets_), uses_bitset,
288 shared_info->language_mode())
289 .ToHandleChecked();
290 DCHECK(!thrower.error());
291 compile_time_ = compile_timer.Elapsed().InMillisecondsF();
292
294
295 RecordHistograms(isolate);
296 ReportCompilationSuccess(script, shared_info->StartPosition(), compile_time_,
297 module_->size());
298 return SUCCEEDED;
299}
300
302 isolate->counters()->asm_module_size_bytes()->AddSample(module_source_size_);
303}
304
305std::unique_ptr<UnoptimizedCompilationJob> AsmJs::NewCompilationJob(
306 ParseInfo* parse_info, FunctionLiteral* literal,
307 AccountingAllocator* allocator) {
308 return std::make_unique<AsmJsCompilationJob>(parse_info, literal, allocator);
309}
310
311namespace {
312inline bool IsValidAsmjsMemorySize(size_t size) {
313 // Enforce asm.js spec minimum size.
314 if (size < (1u << 12u)) return false;
315 // Enforce engine-limited and flag-limited maximum allocation size.
316 if (size > wasm::max_mem32_bytes()) return false;
317 // Enforce power-of-2 sizes for 2^12 - 2^24.
318 if (size < (1u << 24u)) {
319 uint32_t size32 = static_cast<uint32_t>(size);
320 return base::bits::IsPowerOfTwo(size32);
321 }
322 // Enforce multiple of 2^24 for sizes >= 2^24
323 if ((size % (1u << 24u)) != 0) return false;
324 // Limitation of our implementation: for performance reasons, we use unsigned
325 // uint32-to-uintptr extensions for memory addresses, which would give
326 // incorrect behavior for memories larger than 2 GiB.
327 // Note that this does not affect Chrome, which does not allow allocating
328 // larger ArrayBuffers anyway.
329 if (size > 0x8000'0000u) return false;
330 // All checks passed!
331 return true;
332}
333} // namespace
334
339 base::ElapsedTimer instantiate_timer;
340 instantiate_timer.Start();
341 DirectHandle<HeapNumber> uses_bitset(wasm_data->uses_bitset(), isolate);
342 Handle<Script> script(Cast<Script>(shared->script()), isolate);
343 auto* wasm_engine = wasm::GetWasmEngine();
344
345 // Allocate the WasmModuleObject.
347 wasm_engine->FinalizeTranslatedAsmJs(isolate, wasm_data, script);
348
349 // TODO(asmjs): The position currently points to the module definition
350 // but should instead point to the instantiation site (more intuitive).
351 int position = shared->StartPosition();
352
353 // Check that the module is not instantiated as a generator or async function.
354 if (IsResumableFunction(shared->scope_info()->function_kind())) {
355 ReportInstantiationFailure(script, position,
356 "Cannot be instantiated as resumable function");
357 return {};
358 }
359
360 // Check that all used stdlib members are valid.
361 bool stdlib_use_of_typed_array_present = false;
362 wasm::AsmJsParser::StdlibSet stdlib_uses =
363 wasm::AsmJsParser::StdlibSet::FromIntegral(uses_bitset->value_as_bits());
364 if (!stdlib_uses.empty()) { // No checking needed if no uses.
365 if (stdlib.is_null()) {
366 ReportInstantiationFailure(script, position, "Requires standard library");
367 return {};
368 }
369 if (!AreStdlibMembersValid(isolate, stdlib, stdlib_uses,
370 &stdlib_use_of_typed_array_present)) {
371 ReportInstantiationFailure(script, position, "Unexpected stdlib member");
372 return {};
373 }
374 }
375
376 // Check that a valid heap buffer is provided if required.
377 if (stdlib_use_of_typed_array_present) {
378 if (memory.is_null()) {
379 ReportInstantiationFailure(script, position, "Requires heap buffer");
380 return {};
381 }
382 // AsmJs memory must be an ArrayBuffer.
383 if (memory->is_shared()) {
384 ReportInstantiationFailure(script, position,
385 "Invalid heap type: SharedArrayBuffer");
386 return {};
387 }
388 // We don't allow resizable ArrayBuffers because resizable ArrayBuffers may
389 // shrink, and then asm.js does out of bounds memory accesses.
390 if (memory->is_resizable_by_js()) {
391 ReportInstantiationFailure(script, position,
392 "Invalid heap type: resizable ArrayBuffer");
393 return {};
394 }
395 // We don't allow WebAssembly.Memory, because WebAssembly.Memory.grow()
396 // detaches the ArrayBuffer, and that would invalidate the asm.js module.
397 if (memory->GetBackingStore() &&
398 memory->GetBackingStore()->is_wasm_memory()) {
399 ReportInstantiationFailure(script, position,
400 "Invalid heap type: WebAssembly.Memory");
401 return {};
402 }
403 size_t size = memory->byte_length();
404 // Check the asm.js heap size against the valid limits.
405 if (!IsValidAsmjsMemorySize(size)) {
406 ReportInstantiationFailure(script, position, "Invalid heap size");
407 return {};
408 }
409 // Mark the buffer as undetachable. This implies that the buffer cannot be
410 // postMessage()'d, as that detaches the buffer.
411 memory->set_is_detachable(false);
412 } else {
414 }
415
416 wasm::ErrorThrower thrower(isolate, "AsmJs::Instantiate");
418 wasm_engine->SyncInstantiate(isolate, &thrower, module, foreign, memory);
419 if (maybe_instance.is_null()) {
420 // Clear a possible stack overflow from function entry that would have
421 // bypassed the {ErrorThrower}. Be careful not to clear a termination
422 // exception.
423 if (isolate->is_execution_terminating()) return {};
424 if (isolate->has_exception()) isolate->clear_exception();
425 if (thrower.error()) {
426 base::ScopedVector<char> error_reason(100);
427 SNPrintF(error_reason, "Internal wasm failure: %s", thrower.error_msg());
428 ReportInstantiationFailure(script, position, error_reason.begin());
429 } else {
430 ReportInstantiationFailure(script, position, "Internal wasm failure");
431 }
432 thrower.Reset(); // Ensure exceptions do not propagate.
433 return {};
434 }
435 DCHECK(!thrower.error());
436 DirectHandle<WasmInstanceObject> instance = maybe_instance.ToHandleChecked();
437
438 ReportInstantiationSuccess(script, position,
439 instantiate_timer.Elapsed().InMillisecondsF());
440
441 DirectHandle<Name> single_function_name(
442 isolate->factory()->InternalizeUtf8String(AsmJs::kSingleFunctionName));
443 MaybeDirectHandle<Object> single_function =
444 Object::GetProperty(isolate, instance, single_function_name);
445 if (!single_function.is_null() &&
446 !IsUndefined(*single_function.ToHandleChecked(), isolate)) {
447 return single_function;
448 }
449
450 // Here we rely on the fact that the exports object is eagerly created.
451 // The following check is a weak indicator for that. If this ever changes,
452 // then we'll have to call the "exports" getter, and be careful about
453 // handling possible stack overflow exceptions.
454 DCHECK(IsJSObject(instance->exports_object()));
455 return direct_handle(instance->exports_object(), isolate);
456}
457
458} // namespace internal
459} // namespace v8
#define STDLIB_MATH_CONST(cname, const_value)
#define STDLIB_MATH_FUNC(fname, FName, ignore1, ignore2)
#define STDLIB_ARRAY_TYPE(fname, FName)
#define STDLIB_MATH_VALUE_LIST(V)
Definition asm-names.h:9
#define STDLIB_MATH_FUNCTION_LIST(V)
Definition asm-names.h:41
TimeDelta Elapsed() const
static constexpr EnumSet FromIntegral(uint64_t bits)
Definition enum-set.h:91
constexpr bool empty() const
Definition enum-set.h:34
constexpr T ToIntegral() const
Definition enum-set.h:56
double InMillisecondsF() const
Definition time.cc:226
constexpr T * begin() const
Definition vector.h:96
void RecordHistograms(Isolate *isolate)
Definition asm-js.cc:301
AccountingAllocator * allocator_
Definition asm-js.cc:220
wasm::ZoneBuffer * asm_offsets_
Definition asm-js.cc:224
AsmJsCompilationJob(const AsmJsCompilationJob &)=delete
UnoptimizedCompilationInfo compilation_info_
Definition asm-js.cc:222
AsmJsCompilationJob & operator=(const AsmJsCompilationJob &)=delete
wasm::AsmJsParser::StdlibSet stdlib_uses_
Definition asm-js.cc:225
wasm::ZoneBuffer * module_
Definition asm-js.cc:223
AsmJsCompilationJob(ParseInfo *parse_info, FunctionLiteral *literal, AccountingAllocator *allocator)
Definition asm-js.cc:193
Status FinalizeJobImpl(DirectHandle< SharedFunctionInfo > shared_info, Isolate *isolate) final
Definition asm-js.cc:271
static const char *const kSingleFunctionName
Definition asm-js.h:38
static MaybeDirectHandle< Object > InstantiateAsmWasm(Isolate *isolate, DirectHandle< SharedFunctionInfo >, DirectHandle< AsmWasmData > wasm_data, DirectHandle< JSReceiver > stdlib, DirectHandle< JSReceiver > foreign, DirectHandle< JSArrayBuffer > memory)
Definition asm-js.cc:335
static std::unique_ptr< UnoptimizedCompilationJob > NewCompilationJob(ParseInfo *parse_info, FunctionLiteral *literal, AccountingAllocator *allocator)
Definition asm-js.cc:305
V8_INLINE bool is_null() const
Definition handles.h:693
int start_position() const
Definition ast.cc:221
static Handle< Object > GetDataProperty(Isolate *isolate, DirectHandle< JSReceiver > object, DirectHandle< Name > name)
V8_INLINE DirectHandle< T > ToHandleChecked() const
V8_INLINE bool is_null() const
static V8_EXPORT_PRIVATE void ReportMessage(Isolate *isolate, const MessageLocation *loc, DirectHandle< JSMessageObject > message)
Definition messages.cc:98
static V8_EXPORT_PRIVATE Handle< JSMessageObject > MakeMessageObject(Isolate *isolate, MessageTemplate type, const MessageLocation *location, DirectHandle< Object > argument, DirectHandle< StackTraceInfo > stack_trace=DirectHandle< StackTraceInfo >::null())
Definition messages.cc:77
static double NumberValue(Tagged< Number > obj)
V8_EXPORT_PRIVATE static V8_WARN_UNUSED_RESULT MaybeHandle< Object > GetProperty(LookupIterator *it, bool is_global_reference=false)
Definition objects.cc:1248
Utf16CharacterStream * character_stream() const
Definition parse-info.h:288
void SetAsmWasmData(Handle< AsmWasmData > asm_wasm_data)
UnoptimizedCompilationInfo * compilation_info() const
Definition compiler.h:364
virtual bool can_access_heap() const =0
T * New(Args &&... args)
Definition zone.h:114
const StdlibSet * stdlib_uses() const
Definition asm-parser.h:59
base::EnumSet< StandardMember, uint64_t > StdlibSet
Definition asm-parser.h:51
const char * failure_message() const
Definition asm-parser.h:56
WasmModuleBuilder * module_builder()
Definition asm-parser.h:58
MaybeHandle< AsmWasmData > SyncCompileTranslatedAsmJs(Isolate *isolate, ErrorThrower *thrower, base::OwnedVector< const uint8_t > bytes, DirectHandle< Script > script, base::Vector< const uint8_t > asm_js_offset_table_bytes, DirectHandle< HeapNumber > uses_bitset, LanguageMode language_mode)
void WriteTo(ZoneBuffer *buffer) const
void WriteAsmJsOffsetTable(ZoneBuffer *buffer) const
ZoneVector< RpoNumber > & result
FunctionLiteral * literal
Definition liveedit.cc:294
int position
Definition liveedit.cc:290
constexpr bool IsPowerOfTwo(T value)
Definition bits.h:187
int SNPrintF(Vector< char > str, const char *format,...)
Definition strings.cc:20
constexpr Vector< const char > StaticCharVector(const char(&array)[N])
Definition vector.h:326
constexpr Vector< T > VectorOf(T *start, size_t size)
Definition vector.h:360
Vector< const char > CStrVector(const char *data)
Definition vector.h:331
OwnedVector< T > OwnedCopyOf(const T *data, size_t size)
Definition vector.h:383
uint64_t max_mem32_bytes()
WasmEngine * GetWasmEngine()
bool IsNaN(Tagged< Object > obj)
bool IsNumber(Tagged< Object > obj)
bool IsResumableFunction(FunctionKind kind)
V8_INLINE DirectHandle< T > direct_handle(Tagged< T > object, Isolate *isolate)
V8_EXPORT_PRIVATE FlagValues v8_flags
kInterpreterTrampolineOffset script
Tagged< To > Cast(Tagged< From > value, const v8::SourceLocation &loc=INIT_SOURCE_LOCATION_IN_DEBUG)
Definition casting.h:150
Local< T > Handle
#define CHECK_NE(lhs, rhs)
#define DCHECK(condition)
Definition logging.h:482
#define ZONE_NAME
Definition zone.h:22