• 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/asmjs/asm-js.h"
6 
7 #include "src/api-natives.h"
8 #include "src/api.h"
9 #include "src/asmjs/asm-typer.h"
10 #include "src/asmjs/asm-wasm-builder.h"
11 #include "src/assert-scope.h"
12 #include "src/base/platform/elapsed-timer.h"
13 #include "src/compilation-info.h"
14 #include "src/execution.h"
15 #include "src/factory.h"
16 #include "src/handles.h"
17 #include "src/isolate.h"
18 #include "src/objects-inl.h"
19 #include "src/objects.h"
20 #include "src/parsing/parse-info.h"
21 
22 #include "src/wasm/module-decoder.h"
23 #include "src/wasm/wasm-js.h"
24 #include "src/wasm/wasm-module-builder.h"
25 #include "src/wasm/wasm-module.h"
26 #include "src/wasm/wasm-objects.h"
27 #include "src/wasm/wasm-result.h"
28 
29 typedef uint8_t byte;
30 
31 using v8::internal::wasm::ErrorThrower;
32 
33 namespace v8 {
34 namespace internal {
35 
36 namespace {
37 enum WasmDataEntries {
38   kWasmDataCompiledModule,
39   kWasmDataForeignGlobals,
40   kWasmDataUsesArray,
41   kWasmDataScript,
42   kWasmDataScriptPosition,
43   kWasmDataEntryCount,
44 };
45 
StdlibMathMember(i::Isolate * isolate,Handle<JSReceiver> stdlib,Handle<Name> name)46 Handle<i::Object> StdlibMathMember(i::Isolate* isolate,
47                                    Handle<JSReceiver> stdlib,
48                                    Handle<Name> name) {
49   if (stdlib.is_null()) {
50     return Handle<i::Object>();
51   }
52   Handle<i::Name> math_name(
53       isolate->factory()->InternalizeOneByteString(STATIC_CHAR_VECTOR("Math")));
54   MaybeHandle<i::Object> maybe_math = i::Object::GetProperty(stdlib, math_name);
55   if (maybe_math.is_null()) {
56     return Handle<i::Object>();
57   }
58   Handle<i::Object> math = maybe_math.ToHandleChecked();
59   if (!math->IsJSReceiver()) {
60     return Handle<i::Object>();
61   }
62   MaybeHandle<i::Object> maybe_value = i::Object::GetProperty(math, name);
63   if (maybe_value.is_null()) {
64     return Handle<i::Object>();
65   }
66   return maybe_value.ToHandleChecked();
67 }
68 
IsStdlibMemberValid(i::Isolate * isolate,Handle<JSReceiver> stdlib,Handle<i::Object> member_id)69 bool IsStdlibMemberValid(i::Isolate* isolate, Handle<JSReceiver> stdlib,
70                          Handle<i::Object> member_id) {
71   int32_t member_kind;
72   if (!member_id->ToInt32(&member_kind)) {
73     UNREACHABLE();
74   }
75   switch (member_kind) {
76     case wasm::AsmTyper::StandardMember::kNone:
77     case wasm::AsmTyper::StandardMember::kModule:
78     case wasm::AsmTyper::StandardMember::kStdlib:
79     case wasm::AsmTyper::StandardMember::kHeap:
80     case wasm::AsmTyper::StandardMember::kFFI: {
81       // Nothing to check for these.
82       return true;
83     }
84     case wasm::AsmTyper::StandardMember::kInfinity: {
85       if (stdlib.is_null()) {
86         return false;
87       }
88       Handle<i::Name> name(isolate->factory()->InternalizeOneByteString(
89           STATIC_CHAR_VECTOR("Infinity")));
90       MaybeHandle<i::Object> maybe_value = i::Object::GetProperty(stdlib, name);
91       if (maybe_value.is_null()) {
92         return false;
93       }
94       Handle<i::Object> value = maybe_value.ToHandleChecked();
95       return value->IsNumber() && std::isinf(value->Number());
96     }
97     case wasm::AsmTyper::StandardMember::kNaN: {
98       if (stdlib.is_null()) {
99         return false;
100       }
101       Handle<i::Name> name(isolate->factory()->InternalizeOneByteString(
102           STATIC_CHAR_VECTOR("NaN")));
103       MaybeHandle<i::Object> maybe_value = i::Object::GetProperty(stdlib, name);
104       if (maybe_value.is_null()) {
105         return false;
106       }
107       Handle<i::Object> value = maybe_value.ToHandleChecked();
108       return value->IsNaN();
109     }
110 #define STDLIB_MATH_FUNC(CamelName, fname)                             \
111   case wasm::AsmTyper::StandardMember::k##CamelName: {                 \
112     Handle<i::Name> name(isolate->factory()->InternalizeOneByteString( \
113         STATIC_CHAR_VECTOR(#fname)));                                  \
114     Handle<i::Object> value = StdlibMathMember(isolate, stdlib, name); \
115     if (value.is_null() || !value->IsJSFunction()) {                   \
116       return false;                                                    \
117     }                                                                  \
118     Handle<i::JSFunction> func(i::JSFunction::cast(*value));           \
119     return func->shared()->code() ==                                   \
120            isolate->builtins()->builtin(Builtins::k##CamelName);       \
121   }
122       STDLIB_MATH_FUNC(MathAcos, acos)
123       STDLIB_MATH_FUNC(MathAsin, asin)
124       STDLIB_MATH_FUNC(MathAtan, atan)
125       STDLIB_MATH_FUNC(MathCos, cos)
126       STDLIB_MATH_FUNC(MathSin, sin)
127       STDLIB_MATH_FUNC(MathTan, tan)
128       STDLIB_MATH_FUNC(MathExp, exp)
129       STDLIB_MATH_FUNC(MathLog, log)
130       STDLIB_MATH_FUNC(MathCeil, ceil)
131       STDLIB_MATH_FUNC(MathFloor, floor)
132       STDLIB_MATH_FUNC(MathSqrt, sqrt)
133       STDLIB_MATH_FUNC(MathAbs, abs)
134       STDLIB_MATH_FUNC(MathClz32, clz32)
135       STDLIB_MATH_FUNC(MathMin, min)
136       STDLIB_MATH_FUNC(MathMax, max)
137       STDLIB_MATH_FUNC(MathAtan2, atan2)
138       STDLIB_MATH_FUNC(MathPow, pow)
139       STDLIB_MATH_FUNC(MathImul, imul)
140       STDLIB_MATH_FUNC(MathFround, fround)
141 #undef STDLIB_MATH_FUNC
142 #define STDLIB_MATH_CONST(cname, const_value)                             \
143   case wasm::AsmTyper::StandardMember::kMath##cname: {                    \
144     i::Handle<i::Name> name(isolate->factory()->InternalizeOneByteString( \
145         STATIC_CHAR_VECTOR(#cname)));                                     \
146     i::Handle<i::Object> value = StdlibMathMember(isolate, stdlib, name); \
147     return !value.is_null() && value->IsNumber() &&                       \
148            value->Number() == const_value;                                \
149   }
150       STDLIB_MATH_CONST(E, 2.718281828459045)
151       STDLIB_MATH_CONST(LN10, 2.302585092994046)
152       STDLIB_MATH_CONST(LN2, 0.6931471805599453)
153       STDLIB_MATH_CONST(LOG2E, 1.4426950408889634)
154       STDLIB_MATH_CONST(LOG10E, 0.4342944819032518)
155       STDLIB_MATH_CONST(PI, 3.141592653589793)
156       STDLIB_MATH_CONST(SQRT1_2, 0.7071067811865476)
157       STDLIB_MATH_CONST(SQRT2, 1.4142135623730951)
158 #undef STDLIB_MATH_CONST
159     default: { UNREACHABLE(); }
160   }
161   return false;
162 }
163 
164 }  // namespace
165 
CompileAsmViaWasm(CompilationInfo * info)166 MaybeHandle<FixedArray> AsmJs::CompileAsmViaWasm(CompilationInfo* info) {
167   ErrorThrower thrower(info->isolate(), "Asm.js -> WebAssembly conversion");
168   base::ElapsedTimer asm_wasm_timer;
169   asm_wasm_timer.Start();
170   wasm::AsmWasmBuilder builder(info);
171   Handle<FixedArray> foreign_globals;
172   auto asm_wasm_result = builder.Run(&foreign_globals);
173   if (!asm_wasm_result.success) {
174     DCHECK(!info->isolate()->has_pending_exception());
175     if (!FLAG_suppress_asm_messages) {
176       MessageHandler::ReportMessage(info->isolate(),
177                                     builder.typer()->message_location(),
178                                     builder.typer()->error_message());
179     }
180     return MaybeHandle<FixedArray>();
181   }
182   double asm_wasm_time = asm_wasm_timer.Elapsed().InMillisecondsF();
183 
184   wasm::ZoneBuffer* module = asm_wasm_result.module_bytes;
185   wasm::ZoneBuffer* asm_offsets = asm_wasm_result.asm_offset_table;
186   Vector<const byte> asm_offsets_vec(asm_offsets->begin(),
187                                      static_cast<int>(asm_offsets->size()));
188 
189   base::ElapsedTimer compile_timer;
190   compile_timer.Start();
191   MaybeHandle<JSObject> compiled = SyncCompileTranslatedAsmJs(
192       info->isolate(), &thrower,
193       wasm::ModuleWireBytes(module->begin(), module->end()), info->script(),
194       asm_offsets_vec);
195   DCHECK(!compiled.is_null());
196   double compile_time = compile_timer.Elapsed().InMillisecondsF();
197   DCHECK_GE(module->end(), module->begin());
198   uintptr_t wasm_size = module->end() - module->begin();
199 
200   wasm::AsmTyper::StdlibSet uses = builder.typer()->StdlibUses();
201   Handle<FixedArray> uses_array =
202       info->isolate()->factory()->NewFixedArray(static_cast<int>(uses.size()));
203   int count = 0;
204   for (auto i : uses) {
205     uses_array->set(count++, Smi::FromInt(i));
206   }
207 
208   Handle<FixedArray> result =
209       info->isolate()->factory()->NewFixedArray(kWasmDataEntryCount);
210   result->set(kWasmDataCompiledModule, *compiled.ToHandleChecked());
211   result->set(kWasmDataForeignGlobals, *foreign_globals);
212   result->set(kWasmDataUsesArray, *uses_array);
213   result->set(kWasmDataScript, *info->script());
214   result->set(kWasmDataScriptPosition,
215               Smi::FromInt(info->literal()->position()));
216 
217   MessageLocation location(info->script(), info->literal()->position(),
218                            info->literal()->position());
219   char text[100];
220   int length;
221   if (FLAG_predictable) {
222     length = base::OS::SNPrintF(text, arraysize(text), "success");
223   } else {
224     length = base::OS::SNPrintF(
225         text, arraysize(text),
226         "success, asm->wasm: %0.3f ms, compile: %0.3f ms, %" PRIuPTR " bytes",
227         asm_wasm_time, compile_time, wasm_size);
228   }
229   DCHECK_NE(-1, length);
230   USE(length);
231   Handle<String> stext(info->isolate()->factory()->InternalizeUtf8String(text));
232   Handle<JSMessageObject> message = MessageHandler::MakeMessageObject(
233       info->isolate(), MessageTemplate::kAsmJsCompiled, &location, stext,
234       Handle<JSArray>::null());
235   message->set_error_level(v8::Isolate::kMessageInfo);
236   if (!FLAG_suppress_asm_messages && FLAG_trace_asm_time) {
237     MessageHandler::ReportMessage(info->isolate(), &location, message);
238   }
239 
240   return result;
241 }
242 
IsStdlibValid(i::Isolate * isolate,Handle<FixedArray> wasm_data,Handle<JSReceiver> stdlib)243 bool AsmJs::IsStdlibValid(i::Isolate* isolate, Handle<FixedArray> wasm_data,
244                           Handle<JSReceiver> stdlib) {
245   i::Handle<i::FixedArray> uses(
246       i::FixedArray::cast(wasm_data->get(kWasmDataUsesArray)));
247   for (int i = 0; i < uses->length(); ++i) {
248     if (!IsStdlibMemberValid(isolate, stdlib,
249                              uses->GetValueChecked<i::Object>(isolate, i))) {
250       return false;
251     }
252   }
253   return true;
254 }
255 
InstantiateAsmWasm(i::Isolate * isolate,Handle<FixedArray> wasm_data,Handle<JSArrayBuffer> memory,Handle<JSReceiver> foreign)256 MaybeHandle<Object> AsmJs::InstantiateAsmWasm(i::Isolate* isolate,
257                                               Handle<FixedArray> wasm_data,
258                                               Handle<JSArrayBuffer> memory,
259                                               Handle<JSReceiver> foreign) {
260   base::ElapsedTimer instantiate_timer;
261   instantiate_timer.Start();
262   i::Handle<i::WasmModuleObject> module(
263       i::WasmModuleObject::cast(wasm_data->get(kWasmDataCompiledModule)));
264   i::Handle<i::FixedArray> foreign_globals(
265       i::FixedArray::cast(wasm_data->get(kWasmDataForeignGlobals)));
266 
267   ErrorThrower thrower(isolate, "Asm.js -> WebAssembly instantiation");
268 
269   // Create the ffi object for foreign functions {"": foreign}.
270   Handle<JSObject> ffi_object;
271   if (!foreign.is_null()) {
272     Handle<JSFunction> object_function = Handle<JSFunction>(
273         isolate->native_context()->object_function(), isolate);
274     ffi_object = isolate->factory()->NewJSObject(object_function);
275     JSObject::AddProperty(ffi_object, isolate->factory()->empty_string(),
276                           foreign, NONE);
277   }
278 
279   i::MaybeHandle<i::Object> maybe_module_object =
280       i::wasm::SyncInstantiate(isolate, &thrower, module, ffi_object, memory);
281   if (maybe_module_object.is_null()) {
282     return MaybeHandle<Object>();
283   }
284   i::Handle<i::Object> module_object = maybe_module_object.ToHandleChecked();
285 
286   i::Handle<i::Name> init_name(isolate->factory()->InternalizeUtf8String(
287       wasm::AsmWasmBuilder::foreign_init_name));
288   i::Handle<i::Object> init =
289       i::Object::GetProperty(module_object, init_name).ToHandleChecked();
290 
291   i::Handle<i::Object> undefined(isolate->heap()->undefined_value(), isolate);
292   i::Handle<i::Object>* foreign_args_array =
293       new i::Handle<i::Object>[foreign_globals->length()];
294   for (int j = 0; j < foreign_globals->length(); j++) {
295     if (!foreign.is_null()) {
296       i::MaybeHandle<i::Name> name = i::Object::ToName(
297           isolate, i::Handle<i::Object>(foreign_globals->get(j), isolate));
298       if (!name.is_null()) {
299         i::MaybeHandle<i::Object> val =
300             i::Object::GetProperty(foreign, name.ToHandleChecked());
301         if (!val.is_null()) {
302           foreign_args_array[j] = val.ToHandleChecked();
303           continue;
304         }
305       }
306     }
307     foreign_args_array[j] = undefined;
308   }
309   i::MaybeHandle<i::Object> retval = i::Execution::Call(
310       isolate, init, undefined, foreign_globals->length(), foreign_args_array);
311   delete[] foreign_args_array;
312   DCHECK(!retval.is_null());
313 
314   i::Handle<i::Name> single_function_name(
315       isolate->factory()->InternalizeUtf8String(
316           wasm::AsmWasmBuilder::single_function_name));
317   i::MaybeHandle<i::Object> single_function =
318       i::Object::GetProperty(module_object, single_function_name);
319   if (!single_function.is_null() &&
320       !single_function.ToHandleChecked()->IsUndefined(isolate)) {
321     return single_function;
322   }
323 
324   i::Handle<i::Script> script(i::Script::cast(wasm_data->get(kWasmDataScript)));
325   int32_t position = 0;
326   if (!wasm_data->get(kWasmDataScriptPosition)->ToInt32(&position)) {
327     UNREACHABLE();
328   }
329   MessageLocation location(script, position, position);
330   char text[50];
331   int length;
332   if (FLAG_predictable) {
333     length = base::OS::SNPrintF(text, arraysize(text), "success");
334   } else {
335     length = base::OS::SNPrintF(text, arraysize(text), "success, %0.3f ms",
336                                 instantiate_timer.Elapsed().InMillisecondsF());
337   }
338   DCHECK_NE(-1, length);
339   USE(length);
340   Handle<String> stext(isolate->factory()->InternalizeUtf8String(text));
341   Handle<JSMessageObject> message = MessageHandler::MakeMessageObject(
342       isolate, MessageTemplate::kAsmJsInstantiated, &location, stext,
343       Handle<JSArray>::null());
344   message->set_error_level(v8::Isolate::kMessageInfo);
345   if (!FLAG_suppress_asm_messages && FLAG_trace_asm_time) {
346     MessageHandler::ReportMessage(isolate, &location, message);
347   }
348 
349   Handle<String> exports_name =
350       isolate->factory()->InternalizeUtf8String("exports");
351   return i::Object::GetProperty(module_object, exports_name);
352 }
353 
354 }  // namespace internal
355 }  // namespace v8
356