Home | History | Annotate | Download | only in asmjs
      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 
     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 
     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 
    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 
    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 
    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