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
29typedef uint8_t byte;
30
31using v8::internal::wasm::ErrorThrower;
32
33namespace v8 {
34namespace internal {
35
36namespace {
37enum WasmDataEntries {
38  kWasmDataCompiledModule,
39  kWasmDataForeignGlobals,
40  kWasmDataUsesArray,
41  kWasmDataScript,
42  kWasmDataScriptPosition,
43  kWasmDataEntryCount,
44};
45
46Handle<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
69bool 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
166MaybeHandle<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
243bool 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
256MaybeHandle<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