Line data Source code
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 "src/api-natives.h"
8 : #include "src/api.h"
9 : #include "src/asmjs/asm-names.h"
10 : #include "src/asmjs/asm-parser.h"
11 : #include "src/asmjs/asm-typer.h"
12 : #include "src/asmjs/asm-wasm-builder.h"
13 : #include "src/assert-scope.h"
14 : #include "src/base/platform/elapsed-timer.h"
15 : #include "src/compilation-info.h"
16 : #include "src/execution.h"
17 : #include "src/factory.h"
18 : #include "src/handles.h"
19 : #include "src/isolate.h"
20 : #include "src/objects-inl.h"
21 : #include "src/objects.h"
22 :
23 : #include "src/wasm/module-decoder.h"
24 : #include "src/wasm/wasm-js.h"
25 : #include "src/wasm/wasm-module-builder.h"
26 : #include "src/wasm/wasm-module.h"
27 : #include "src/wasm/wasm-objects.h"
28 : #include "src/wasm/wasm-result.h"
29 :
30 : namespace v8 {
31 : namespace internal {
32 :
33 : namespace {
34 : enum WasmDataEntries {
35 : kWasmDataCompiledModule,
36 : kWasmDataForeignGlobals,
37 : kWasmDataUsesArray,
38 : kWasmDataScript,
39 : kWasmDataScriptPosition,
40 : kWasmDataEntryCount,
41 : };
42 :
43 14434 : Handle<Object> StdlibMathMember(Isolate* isolate, Handle<JSReceiver> stdlib,
44 : Handle<Name> name) {
45 14434 : if (stdlib.is_null()) {
46 0 : return Handle<Object>();
47 : }
48 : Handle<Name> math_name(
49 28868 : isolate->factory()->InternalizeOneByteString(STATIC_CHAR_VECTOR("Math")));
50 14434 : MaybeHandle<Object> maybe_math = Object::GetProperty(stdlib, math_name);
51 14434 : if (maybe_math.is_null()) {
52 0 : return Handle<Object>();
53 : }
54 : Handle<Object> math = maybe_math.ToHandleChecked();
55 14434 : if (!math->IsJSReceiver()) {
56 216 : return Handle<Object>();
57 : }
58 14218 : MaybeHandle<Object> maybe_value = Object::GetProperty(math, name);
59 14218 : if (maybe_value.is_null()) {
60 0 : return Handle<Object>();
61 : }
62 : return maybe_value.ToHandleChecked();
63 : }
64 :
65 16704 : bool IsStdlibMemberValid(Isolate* isolate, Handle<JSReceiver> stdlib,
66 : Handle<Object> member_id) {
67 : int32_t member_kind;
68 16704 : if (!member_id->ToInt32(&member_kind)) {
69 0 : UNREACHABLE();
70 : }
71 16704 : switch (static_cast<wasm::AsmTyper::StandardMember>(member_kind)) {
72 : case wasm::AsmTyper::StandardMember::kNone:
73 : case wasm::AsmTyper::StandardMember::kModule:
74 : case wasm::AsmTyper::StandardMember::kStdlib:
75 : case wasm::AsmTyper::StandardMember::kHeap:
76 : case wasm::AsmTyper::StandardMember::kFFI: {
77 : // Nothing to check for these.
78 : return true;
79 : }
80 : case wasm::AsmTyper::StandardMember::kInfinity: {
81 31 : if (stdlib.is_null()) {
82 : return false;
83 : }
84 : Handle<Name> name(isolate->factory()->InternalizeOneByteString(
85 62 : STATIC_CHAR_VECTOR("Infinity")));
86 31 : MaybeHandle<Object> maybe_value = Object::GetProperty(stdlib, name);
87 31 : if (maybe_value.is_null()) {
88 : return false;
89 : }
90 : Handle<Object> value = maybe_value.ToHandleChecked();
91 54 : return value->IsNumber() && std::isinf(value->Number());
92 : }
93 : case wasm::AsmTyper::StandardMember::kNaN: {
94 85 : if (stdlib.is_null()) {
95 : return false;
96 : }
97 : Handle<Name> name(isolate->factory()->InternalizeOneByteString(
98 90 : STATIC_CHAR_VECTOR("NaN")));
99 45 : MaybeHandle<Object> maybe_value = Object::GetProperty(stdlib, name);
100 45 : if (maybe_value.is_null()) {
101 : return false;
102 : }
103 : Handle<Object> value = maybe_value.ToHandleChecked();
104 45 : return value->IsNaN();
105 : }
106 : #define STDLIB_MATH_FUNC(fname, FName, ignore1, ignore2) \
107 : case wasm::AsmTyper::StandardMember::kMath##FName: { \
108 : Handle<Name> name(isolate->factory()->InternalizeOneByteString( \
109 : STATIC_CHAR_VECTOR(#fname))); \
110 : Handle<Object> value = StdlibMathMember(isolate, stdlib, name); \
111 : if (value.is_null() || !value->IsJSFunction()) { \
112 : return false; \
113 : } \
114 : Handle<JSFunction> func(JSFunction::cast(*value)); \
115 : return func->shared()->code() == \
116 : isolate->builtins()->builtin(Builtins::kMath##FName); \
117 : }
118 56288 : STDLIB_MATH_FUNCTION_LIST(STDLIB_MATH_FUNC)
119 : #undef STDLIB_MATH_FUNC
120 : #define STDLIB_MATH_CONST(cname, const_value) \
121 : case wasm::AsmTyper::StandardMember::kMath##cname: { \
122 : Handle<Name> name(isolate->factory()->InternalizeOneByteString( \
123 : STATIC_CHAR_VECTOR(#cname))); \
124 : Handle<Object> value = StdlibMathMember(isolate, stdlib, name); \
125 : return !value.is_null() && value->IsNumber() && \
126 : value->Number() == const_value; \
127 : }
128 864 : STDLIB_MATH_VALUE_LIST(STDLIB_MATH_CONST)
129 : #undef STDLIB_MATH_CONST
130 : #define STDLIB_ARRAY_TYPE(fname, FName) \
131 : case wasm::AsmTyper::StandardMember::k##FName: { \
132 : if (stdlib.is_null()) { \
133 : return false; \
134 : } \
135 : Handle<Name> name(isolate->factory()->InternalizeOneByteString( \
136 : STATIC_CHAR_VECTOR(#FName))); \
137 : Handle<Object> value; \
138 : MaybeHandle<Object> maybe_value = Object::GetProperty(stdlib, name); \
139 : if (!maybe_value.ToHandle(&value) || !value->IsJSFunction()) { \
140 : return false; \
141 : } \
142 : Handle<JSFunction> func = Handle<JSFunction>::cast(value); \
143 : return func.is_identical_to(isolate->fname()); \
144 : }
145 112 : STDLIB_ARRAY_TYPE(int8_array_fun, Int8Array)
146 105 : STDLIB_ARRAY_TYPE(uint8_array_fun, Uint8Array)
147 98 : STDLIB_ARRAY_TYPE(int16_array_fun, Int16Array)
148 84 : STDLIB_ARRAY_TYPE(uint16_array_fun, Uint16Array)
149 896 : STDLIB_ARRAY_TYPE(int32_array_fun, Int32Array)
150 84 : STDLIB_ARRAY_TYPE(uint32_array_fun, Uint32Array)
151 182 : STDLIB_ARRAY_TYPE(float32_array_fun, Float32Array)
152 119 : STDLIB_ARRAY_TYPE(float64_array_fun, Float64Array)
153 : #undef STDLIB_ARRAY_TYPE
154 : }
155 0 : UNREACHABLE();
156 : return false;
157 : }
158 :
159 : } // namespace
160 :
161 1087847 : MaybeHandle<FixedArray> AsmJs::CompileAsmViaWasm(CompilationInfo* info) {
162 10848 : wasm::ZoneBuffer* module = nullptr;
163 3616 : wasm::ZoneBuffer* asm_offsets = nullptr;
164 : Handle<FixedArray> uses_array;
165 : Handle<FixedArray> foreign_globals;
166 : base::ElapsedTimer asm_wasm_timer;
167 : asm_wasm_timer.Start();
168 354696 : wasm::AsmWasmBuilder builder(info);
169 354695 : size_t asm_wasm_zone_start = info->zone()->allocation_size();
170 354695 : if (FLAG_fast_validate_asm) {
171 : wasm::AsmJsParser parser(info->isolate(), info->zone(), info->script(),
172 : info->literal()->start_position(),
173 702327 : info->literal()->end_position());
174 351164 : if (!parser.Run()) {
175 : DCHECK(!info->isolate()->has_pending_exception());
176 350615 : if (!FLAG_suppress_asm_messages) {
177 : MessageLocation location(info->script(), parser.failure_location(),
178 139 : parser.failure_location());
179 : Handle<String> message =
180 : info->isolate()
181 : ->factory()
182 : ->NewStringFromUtf8(CStrVector(parser.failure_message()))
183 417 : .ToHandleChecked();
184 : Handle<JSMessageObject> error_message =
185 : MessageHandler::MakeMessageObject(
186 : info->isolate(), MessageTemplate::kAsmJsInvalid, &location,
187 139 : message, Handle<FixedArray>::null());
188 : error_message->set_error_level(v8::Isolate::kMessageWarning);
189 : MessageHandler::ReportMessage(info->isolate(), &location,
190 139 : error_message);
191 : }
192 350615 : return MaybeHandle<FixedArray>();
193 : }
194 : Zone* zone = info->zone();
195 : module = new (zone) wasm::ZoneBuffer(zone);
196 549 : parser.module_builder()->WriteTo(*module);
197 : asm_offsets = new (zone) wasm::ZoneBuffer(zone);
198 549 : parser.module_builder()->WriteAsmJsOffsetTable(*asm_offsets);
199 : // TODO(bradnelson): Remove foreign_globals plumbing (as we don't need it
200 : // for the new parser).
201 549 : foreign_globals = info->isolate()->factory()->NewFixedArray(0);
202 : uses_array = info->isolate()->factory()->NewFixedArray(
203 1098 : static_cast<int>(parser.stdlib_uses()->size()));
204 : int count = 0;
205 2309 : for (auto i : *parser.stdlib_uses()) {
206 1211 : uses_array->set(count++, Smi::FromInt(i));
207 549 : }
208 : } else {
209 3532 : auto asm_wasm_result = builder.Run(&foreign_globals);
210 3532 : if (!asm_wasm_result.success) {
211 : DCHECK(!info->isolate()->has_pending_exception());
212 465 : if (!FLAG_suppress_asm_messages) {
213 : MessageHandler::ReportMessage(info->isolate(),
214 : builder.typer()->message_location(),
215 465 : builder.typer()->error_message());
216 : }
217 465 : return MaybeHandle<FixedArray>();
218 : }
219 3067 : module = asm_wasm_result.module_bytes;
220 3067 : asm_offsets = asm_wasm_result.asm_offset_table;
221 : wasm::AsmTyper::StdlibSet uses = builder.typer()->StdlibUses();
222 : uses_array = info->isolate()->factory()->NewFixedArray(
223 6134 : static_cast<int>(uses.size()));
224 : int count = 0;
225 14434 : for (auto i : uses) {
226 8300 : uses_array->set(count++, Smi::FromInt(i));
227 : }
228 : }
229 :
230 3616 : double asm_wasm_time = asm_wasm_timer.Elapsed().InMillisecondsF();
231 3616 : size_t asm_wasm_zone = info->zone()->allocation_size() - asm_wasm_zone_start;
232 3616 : if (FLAG_trace_asm_parser) {
233 : PrintF("[asm.js translation successful: time=%0.3fms, zone=%" PRIuS "KB]\n",
234 0 : asm_wasm_time, asm_wasm_zone / KB);
235 : }
236 :
237 : Vector<const byte> asm_offsets_vec(asm_offsets->begin(),
238 3616 : static_cast<int>(asm_offsets->size()));
239 :
240 : base::ElapsedTimer compile_timer;
241 : compile_timer.Start();
242 : wasm::ErrorThrower thrower(info->isolate(),
243 3616 : "Asm.js -> WebAssembly conversion");
244 : MaybeHandle<JSObject> compiled = SyncCompileTranslatedAsmJs(
245 : info->isolate(), &thrower,
246 : wasm::ModuleWireBytes(module->begin(), module->end()), info->script(),
247 10848 : asm_offsets_vec);
248 : DCHECK(!compiled.is_null());
249 : DCHECK(!thrower.error());
250 3616 : double compile_time = compile_timer.Elapsed().InMillisecondsF();
251 : DCHECK_GE(module->end(), module->begin());
252 7232 : uintptr_t wasm_size = module->end() - module->begin();
253 :
254 : Handle<FixedArray> result =
255 3616 : info->isolate()->factory()->NewFixedArray(kWasmDataEntryCount);
256 3616 : result->set(kWasmDataCompiledModule, *compiled.ToHandleChecked());
257 3616 : result->set(kWasmDataForeignGlobals, *foreign_globals);
258 3616 : result->set(kWasmDataUsesArray, *uses_array);
259 7232 : result->set(kWasmDataScript, *info->script());
260 : result->set(kWasmDataScriptPosition,
261 3616 : Smi::FromInt(info->literal()->position()));
262 :
263 3616 : MessageLocation location(info->script(), info->literal()->position(),
264 7232 : info->literal()->position());
265 : char text[100];
266 : int length;
267 3616 : if (FLAG_predictable) {
268 0 : length = base::OS::SNPrintF(text, arraysize(text), "success");
269 : } else {
270 : length = base::OS::SNPrintF(
271 : text, arraysize(text),
272 : "success, asm->wasm: %0.3f ms, compile: %0.3f ms, %" PRIuPTR " bytes",
273 3616 : asm_wasm_time, compile_time, wasm_size);
274 : }
275 : DCHECK_NE(-1, length);
276 : USE(length);
277 3616 : Handle<String> stext(info->isolate()->factory()->InternalizeUtf8String(text));
278 : Handle<JSMessageObject> message = MessageHandler::MakeMessageObject(
279 : info->isolate(), MessageTemplate::kAsmJsCompiled, &location, stext,
280 3616 : Handle<FixedArray>::null());
281 : message->set_error_level(v8::Isolate::kMessageInfo);
282 3616 : if (!FLAG_suppress_asm_messages && FLAG_trace_asm_time) {
283 0 : MessageHandler::ReportMessage(info->isolate(), &location, message);
284 : }
285 :
286 : return result;
287 : }
288 :
289 6994 : bool AsmJs::IsStdlibValid(Isolate* isolate, Handle<FixedArray> wasm_data,
290 : Handle<JSReceiver> stdlib) {
291 : Handle<FixedArray> uses(FixedArray::cast(wasm_data->get(kWasmDataUsesArray)));
292 46386 : for (int i = 0; i < uses->length(); ++i) {
293 16704 : if (!IsStdlibMemberValid(isolate, stdlib,
294 16704 : uses->GetValueChecked<Object>(isolate, i))) {
295 : return false;
296 : }
297 : }
298 : return true;
299 : }
300 :
301 6489 : MaybeHandle<Object> AsmJs::InstantiateAsmWasm(Isolate* isolate,
302 : Handle<FixedArray> wasm_data,
303 : Handle<JSArrayBuffer> memory,
304 : Handle<JSReceiver> foreign) {
305 : base::ElapsedTimer instantiate_timer;
306 : instantiate_timer.Start();
307 : Handle<WasmModuleObject> module(
308 6489 : WasmModuleObject::cast(wasm_data->get(kWasmDataCompiledModule)));
309 : Handle<FixedArray> foreign_globals(
310 : FixedArray::cast(wasm_data->get(kWasmDataForeignGlobals)));
311 :
312 : // Create the ffi object for foreign functions {"": foreign}.
313 : Handle<JSObject> ffi_object;
314 6489 : if (!foreign.is_null()) {
315 : Handle<JSFunction> object_function = Handle<JSFunction>(
316 5076 : isolate->native_context()->object_function(), isolate);
317 2538 : ffi_object = isolate->factory()->NewJSObject(object_function);
318 : JSObject::AddProperty(ffi_object, isolate->factory()->empty_string(),
319 2538 : foreign, NONE);
320 : }
321 :
322 : wasm::ErrorThrower thrower(isolate, "Asm.js -> WebAssembly instantiation");
323 : MaybeHandle<Object> maybe_module_object =
324 12978 : wasm::SyncInstantiate(isolate, &thrower, module, ffi_object, memory);
325 6489 : if (maybe_module_object.is_null()) {
326 : thrower.Reify(); // Ensure exceptions do not propagate.
327 : return MaybeHandle<Object>();
328 : }
329 : DCHECK(!thrower.error());
330 6487 : Handle<Object> module_object = maybe_module_object.ToHandleChecked();
331 :
332 6487 : if (!FLAG_fast_validate_asm) {
333 : Handle<Name> init_name(isolate->factory()->InternalizeUtf8String(
334 5796 : wasm::AsmWasmBuilder::foreign_init_name));
335 : Handle<Object> init =
336 11592 : Object::GetProperty(module_object, init_name).ToHandleChecked();
337 :
338 5796 : Handle<Object> undefined(isolate->heap()->undefined_value(), isolate);
339 : Handle<Object>* foreign_args_array =
340 7558 : new Handle<Object>[foreign_globals->length()];
341 9320 : for (int j = 0; j < foreign_globals->length(); j++) {
342 1762 : if (!foreign.is_null()) {
343 : MaybeHandle<Name> name = Object::ToName(
344 1750 : isolate, Handle<Object>(foreign_globals->get(j), isolate));
345 1750 : if (!name.is_null()) {
346 : MaybeHandle<Object> val =
347 1750 : Object::GetProperty(foreign, name.ToHandleChecked());
348 1750 : if (!val.is_null()) {
349 3500 : foreign_args_array[j] = val.ToHandleChecked();
350 : continue;
351 : }
352 : }
353 : }
354 12 : foreign_args_array[j] = undefined;
355 : }
356 : MaybeHandle<Object> retval =
357 : Execution::Call(isolate, init, undefined, foreign_globals->length(),
358 5796 : foreign_args_array);
359 5796 : delete[] foreign_args_array;
360 : DCHECK(!retval.is_null());
361 : }
362 :
363 : Handle<Name> single_function_name(isolate->factory()->InternalizeUtf8String(
364 6487 : wasm::AsmWasmBuilder::single_function_name));
365 : MaybeHandle<Object> single_function =
366 6487 : Object::GetProperty(module_object, single_function_name);
367 12974 : if (!single_function.is_null() &&
368 : !single_function.ToHandleChecked()->IsUndefined(isolate)) {
369 254 : return single_function;
370 : }
371 :
372 : Handle<Script> script(Script::cast(wasm_data->get(kWasmDataScript)));
373 6233 : int32_t position = 0;
374 6233 : if (!wasm_data->get(kWasmDataScriptPosition)->ToInt32(&position)) {
375 0 : UNREACHABLE();
376 : }
377 6233 : MessageLocation location(script, position, position);
378 : char text[50];
379 : int length;
380 6233 : if (FLAG_predictable) {
381 0 : length = base::OS::SNPrintF(text, arraysize(text), "success");
382 : } else {
383 : length = base::OS::SNPrintF(text, arraysize(text), "success, %0.3f ms",
384 6233 : instantiate_timer.Elapsed().InMillisecondsF());
385 : }
386 : DCHECK_NE(-1, length);
387 : USE(length);
388 6233 : Handle<String> stext(isolate->factory()->InternalizeUtf8String(text));
389 : Handle<JSMessageObject> message = MessageHandler::MakeMessageObject(
390 : isolate, MessageTemplate::kAsmJsInstantiated, &location, stext,
391 6233 : Handle<FixedArray>::null());
392 : message->set_error_level(v8::Isolate::kMessageInfo);
393 6233 : if (!FLAG_suppress_asm_messages && FLAG_trace_asm_time) {
394 0 : MessageHandler::ReportMessage(isolate, &location, message);
395 : }
396 :
397 : Handle<String> exports_name =
398 6233 : isolate->factory()->InternalizeUtf8String("exports");
399 6233 : return Object::GetProperty(module_object, exports_name);
400 : }
401 :
402 : } // namespace internal
403 : } // namespace v8
|