LCOV - code coverage report
Current view: top level - src/asmjs - asm-js.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 124 133 93.2 %
Date: 2017-10-20 Functions: 14 15 93.3 %

          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/asmjs/asm-names.h"
       8             : #include "src/asmjs/asm-parser.h"
       9             : #include "src/assert-scope.h"
      10             : #include "src/ast/ast.h"
      11             : #include "src/base/optional.h"
      12             : #include "src/base/platform/elapsed-timer.h"
      13             : #include "src/compilation-info.h"
      14             : #include "src/compiler.h"
      15             : #include "src/execution.h"
      16             : #include "src/factory.h"
      17             : #include "src/handles.h"
      18             : #include "src/isolate.h"
      19             : #include "src/objects-inl.h"
      20             : #include "src/parsing/parse-info.h"
      21             : #include "src/parsing/scanner-character-streams.h"
      22             : #include "src/parsing/scanner.h"
      23             : 
      24             : #include "src/wasm/module-compiler.h"
      25             : #include "src/wasm/module-decoder.h"
      26             : #include "src/wasm/wasm-js.h"
      27             : #include "src/wasm/wasm-module-builder.h"
      28             : #include "src/wasm/wasm-objects-inl.h"
      29             : #include "src/wasm/wasm-result.h"
      30             : 
      31             : namespace v8 {
      32             : namespace internal {
      33             : 
      34             : const char* const AsmJs::kSingleFunctionName = "__single_function__";
      35             : 
      36             : namespace {
      37             : enum WasmDataEntries {
      38             :   kWasmDataCompiledModule,
      39             :   kWasmDataUsesBitSet,
      40             :   kWasmDataEntryCount,
      41             : };
      42             : 
      43        9831 : Handle<Object> StdlibMathMember(Isolate* isolate, Handle<JSReceiver> stdlib,
      44             :                                 Handle<Name> name) {
      45             :   Handle<Name> math_name(
      46       19662 :       isolate->factory()->InternalizeOneByteString(STATIC_CHAR_VECTOR("Math")));
      47        9831 :   Handle<Object> math = JSReceiver::GetDataProperty(stdlib, math_name);
      48        9837 :   if (!math->IsJSReceiver()) return isolate->factory()->undefined_value();
      49        9825 :   Handle<JSReceiver> math_receiver = Handle<JSReceiver>::cast(math);
      50        9825 :   Handle<Object> value = JSReceiver::GetDataProperty(math_receiver, name);
      51        9825 :   return value;
      52             : }
      53             : 
      54        2835 : bool AreStdlibMembersValid(Isolate* isolate, Handle<JSReceiver> stdlib,
      55             :                            wasm::AsmJsParser::StdlibSet members,
      56             :                            bool* is_typed_array) {
      57        2835 :   if (members.Contains(wasm::AsmJsParser::StandardMember::kInfinity)) {
      58             :     members.Remove(wasm::AsmJsParser::StandardMember::kInfinity);
      59             :     Handle<Name> name = isolate->factory()->Infinity_string();
      60          10 :     Handle<Object> value = JSReceiver::GetDataProperty(stdlib, name);
      61          20 :     if (!value->IsNumber() || !std::isinf(value->Number())) return false;
      62             :   }
      63        2835 :   if (members.Contains(wasm::AsmJsParser::StandardMember::kNaN)) {
      64             :     members.Remove(wasm::AsmJsParser::StandardMember::kNaN);
      65             :     Handle<Name> name = isolate->factory()->NaN_string();
      66          22 :     Handle<Object> value = JSReceiver::GetDataProperty(stdlib, name);
      67          22 :     if (!value->IsNaN()) return false;
      68             :   }
      69             : #define STDLIB_MATH_FUNC(fname, FName, ignore1, ignore2)                   \
      70             :   if (members.Contains(wasm::AsmJsParser::StandardMember::kMath##FName)) { \
      71             :     members.Remove(wasm::AsmJsParser::StandardMember::kMath##FName);       \
      72             :     Handle<Name> name(isolate->factory()->InternalizeOneByteString(        \
      73             :         STATIC_CHAR_VECTOR(#fname)));                                      \
      74             :     Handle<Object> value = StdlibMathMember(isolate, stdlib, name);        \
      75             :     if (!value->IsJSFunction()) return false;                              \
      76             :     Handle<JSFunction> func = Handle<JSFunction>::cast(value);             \
      77             :     if (func->shared()->code() !=                                          \
      78             :         isolate->builtins()->builtin(Builtins::kMath##FName)) {            \
      79             :       return false;                                                        \
      80             :     }                                                                      \
      81             :   }
      82       92773 :   STDLIB_MATH_FUNCTION_LIST(STDLIB_MATH_FUNC)
      83             : #undef STDLIB_MATH_FUNC
      84             : #define STDLIB_MATH_CONST(cname, const_value)                               \
      85             :   if (members.Contains(wasm::AsmJsParser::StandardMember::kMath##cname)) {  \
      86             :     members.Remove(wasm::AsmJsParser::StandardMember::kMath##cname);        \
      87             :     Handle<Name> name(isolate->factory()->InternalizeOneByteString(         \
      88             :         STATIC_CHAR_VECTOR(#cname)));                                       \
      89             :     Handle<Object> value = StdlibMathMember(isolate, stdlib, name);         \
      90             :     if (!value->IsNumber() || value->Number() != const_value) return false; \
      91             :   }
      92       23066 :   STDLIB_MATH_VALUE_LIST(STDLIB_MATH_CONST)
      93             : #undef STDLIB_MATH_CONST
      94             : #define STDLIB_ARRAY_TYPE(fname, FName)                                \
      95             :   if (members.Contains(wasm::AsmJsParser::StandardMember::k##FName)) { \
      96             :     members.Remove(wasm::AsmJsParser::StandardMember::k##FName);       \
      97             :     *is_typed_array = true;                                            \
      98             :     Handle<Name> name(isolate->factory()->InternalizeOneByteString(    \
      99             :         STATIC_CHAR_VECTOR(#FName)));                                  \
     100             :     Handle<Object> value = JSReceiver::GetDataProperty(stdlib, name);  \
     101             :     if (!value->IsJSFunction()) return false;                          \
     102             :     Handle<JSFunction> func = Handle<JSFunction>::cast(value);         \
     103             :     if (!func.is_identical_to(isolate->fname())) return false;         \
     104             :   }
     105        3697 :   STDLIB_ARRAY_TYPE(int8_array_fun, Int8Array)
     106        3516 :   STDLIB_ARRAY_TYPE(uint8_array_fun, Uint8Array)
     107        3541 :   STDLIB_ARRAY_TYPE(int16_array_fun, Int16Array)
     108        3411 :   STDLIB_ARRAY_TYPE(uint16_array_fun, Uint16Array)
     109        8143 :   STDLIB_ARRAY_TYPE(int32_array_fun, Int32Array)
     110        3561 :   STDLIB_ARRAY_TYPE(uint32_array_fun, Uint32Array)
     111        4141 :   STDLIB_ARRAY_TYPE(float32_array_fun, Float32Array)
     112        3555 :   STDLIB_ARRAY_TYPE(float64_array_fun, Float64Array)
     113             : #undef STDLIB_ARRAY_TYPE
     114             :   // All members accounted for.
     115             :   DCHECK(members.IsEmpty());
     116             :   return true;
     117             : }
     118             : 
     119        1592 : void Report(Handle<Script> script, int position, Vector<const char> text,
     120             :             MessageTemplate::Template message_template,
     121             :             v8::Isolate::MessageErrorLevel level) {
     122             :   Isolate* isolate = script->GetIsolate();
     123        1592 :   MessageLocation location(script, position, position);
     124        1592 :   Handle<String> text_object = isolate->factory()->InternalizeUtf8String(text);
     125             :   Handle<JSMessageObject> message = MessageHandler::MakeMessageObject(
     126             :       isolate, message_template, &location, text_object,
     127        1592 :       Handle<FixedArray>::null());
     128        1592 :   message->set_error_level(level);
     129        1592 :   MessageHandler::ReportMessage(isolate, &location, message);
     130        1592 : }
     131             : 
     132             : // Hook to report successful execution of {AsmJs::CompileAsmViaWasm} phase.
     133        3547 : void ReportCompilationSuccess(Handle<Script> script, int position,
     134             :                               double translate_time, double compile_time,
     135             :                               size_t module_size) {
     136        7094 :   if (FLAG_suppress_asm_messages || !FLAG_trace_asm_time) return;
     137             :   EmbeddedVector<char, 100> text;
     138             :   int length = SNPrintF(
     139             :       text, "success, asm->wasm: %0.3f ms, compile: %0.3f ms, %" PRIuS " bytes",
     140           0 :       translate_time, compile_time, module_size);
     141           0 :   CHECK_NE(-1, length);
     142           0 :   text.Truncate(length);
     143             :   Report(script, position, text, MessageTemplate::kAsmJsCompiled,
     144           0 :          v8::Isolate::kMessageInfo);
     145             : }
     146             : 
     147             : // Hook to report failed execution of {AsmJs::CompileAsmViaWasm} phase.
     148      342501 : void ReportCompilationFailure(Handle<Script> script, int position,
     149             :                               const char* reason) {
     150      683565 :   if (FLAG_suppress_asm_messages) return;
     151        1437 :   Vector<const char> text = CStrVector(reason);
     152             :   Report(script, position, text, MessageTemplate::kAsmJsInvalid,
     153        1437 :          v8::Isolate::kMessageWarning);
     154             : }
     155             : 
     156             : // Hook to report successful execution of {AsmJs::InstantiateAsmWasm} phase.
     157        6183 : void ReportInstantiationSuccess(Handle<Script> script, int position,
     158             :                                 double instantiate_time) {
     159       12366 :   if (FLAG_suppress_asm_messages || !FLAG_trace_asm_time) return;
     160             :   EmbeddedVector<char, 50> text;
     161           0 :   int length = SNPrintF(text, "success, %0.3f ms", instantiate_time);
     162           0 :   CHECK_NE(-1, length);
     163           0 :   text.Truncate(length);
     164             :   Report(script, position, text, MessageTemplate::kAsmJsInstantiated,
     165           0 :          v8::Isolate::kMessageInfo);
     166             : }
     167             : 
     168             : // Hook to report failed execution of {AsmJs::InstantiateAsmWasm} phase.
     169         182 : void ReportInstantiationFailure(Handle<Script> script, int position,
     170             :                                 const char* reason) {
     171         209 :   if (FLAG_suppress_asm_messages) return;
     172         155 :   Vector<const char> text = CStrVector(reason);
     173             :   Report(script, position, text, MessageTemplate::kAsmJsLinkingFailed,
     174         155 :          v8::Isolate::kMessageWarning);
     175             : }
     176             : 
     177             : }  // namespace
     178             : 
     179             : // The compilation of asm.js modules is split into two distinct steps:
     180             : //  [1] ExecuteJobImpl: The asm.js module source is parsed, validated, and
     181             : //      translated to a valid WebAssembly module. The result are two vectors
     182             : //      representing the encoded module as well as encoded source position
     183             : //      information and a StdlibSet bit set.
     184             : //  [2] FinalizeJobImpl: The module is handed to WebAssembly which decodes it
     185             : //      into an internal representation and eventually compiles it to machine
     186             : //      code.
     187      692095 : class AsmJsCompilationJob final : public CompilationJob {
     188             :  public:
     189      346048 :   explicit AsmJsCompilationJob(ParseInfo* parse_info, FunctionLiteral* literal,
     190      346048 :                                Isolate* isolate)
     191             :       : CompilationJob(isolate, parse_info, &compilation_info_, "AsmJs"),
     192             :         zone_(isolate->allocator(), ZONE_NAME),
     193             :         compilation_info_(&zone_, isolate, parse_info, literal),
     194             :         module_(nullptr),
     195             :         asm_offsets_(nullptr),
     196             :         translate_time_(0),
     197     1038144 :         compile_time_(0) {}
     198             : 
     199             :  protected:
     200             :   Status PrepareJobImpl() final;
     201             :   Status ExecuteJobImpl() final;
     202             :   Status FinalizeJobImpl() final;
     203             : 
     204             :  private:
     205             :   Zone zone_;
     206             :   CompilationInfo compilation_info_;
     207             :   wasm::ZoneBuffer* module_;
     208             :   wasm::ZoneBuffer* asm_offsets_;
     209             :   wasm::AsmJsParser::StdlibSet stdlib_uses_;
     210             : 
     211             :   double translate_time_;  // Time (milliseconds) taken to execute step [1].
     212             :   double compile_time_;    // Time (milliseconds) taken to execute step [2].
     213             : 
     214             :   DISALLOW_COPY_AND_ASSIGN(AsmJsCompilationJob);
     215             : };
     216             : 
     217      346046 : CompilationJob::Status AsmJsCompilationJob::PrepareJobImpl() {
     218      346046 :   return SUCCEEDED;
     219             : }
     220             : 
     221      346045 : CompilationJob::Status AsmJsCompilationJob::ExecuteJobImpl() {
     222             :   // Step 1: Translate asm.js module to WebAssembly module.
     223             :   HistogramTimerScope translate_time_scope(
     224     2782565 :       compilation_info()->isolate()->counters()->asm_wasm_translation_time());
     225      346047 :   size_t compile_zone_start = compilation_info()->zone()->allocation_size();
     226             :   base::ElapsedTimer translate_timer;
     227             :   translate_timer.Start();
     228             : 
     229      346048 :   Zone* compile_zone = compilation_info()->zone();
     230      692095 :   Zone translate_zone(compilation_info()->isolate()->allocator(), ZONE_NAME);
     231             : 
     232             :   Utf16CharacterStream* stream = parse_info()->character_stream();
     233             :   base::Optional<AllowHandleDereference> allow_deref;
     234             :   if (stream->can_access_heap()) {
     235             :     DCHECK(
     236             :         ThreadId::Current().Equals(compilation_info()->isolate()->thread_id()));
     237             :     allow_deref.emplace();
     238             :   }
     239      346048 :   stream->Seek(compilation_info()->literal()->start_position());
     240      346048 :   wasm::AsmJsParser parser(&translate_zone, stack_limit(), stream);
     241      346047 :   if (!parser.Run()) {
     242             :     // TODO(rmcilroy): Temporarily allow heap access here until we have a
     243             :     // mechanism for delaying pending messages.
     244             :     DCHECK(
     245             :         ThreadId::Current().Equals(compilation_info()->isolate()->thread_id()));
     246             :     AllowHeapAllocation allow_allocation;
     247             :     AllowHandleAllocation allow_handles;
     248             :     allow_deref.emplace();
     249             : 
     250             :     DCHECK(!compilation_info()->isolate()->has_pending_exception());
     251             :     ReportCompilationFailure(parse_info()->script(), parser.failure_location(),
     252      685002 :                              parser.failure_message());
     253             :     return FAILED;
     254             :   }
     255        3547 :   module_ = new (compile_zone) wasm::ZoneBuffer(compile_zone);
     256        3547 :   parser.module_builder()->WriteTo(*module_);
     257        3547 :   asm_offsets_ = new (compile_zone) wasm::ZoneBuffer(compile_zone);
     258        3547 :   parser.module_builder()->WriteAsmJsOffsetTable(*asm_offsets_);
     259        3547 :   stdlib_uses_ = *parser.stdlib_uses();
     260             : 
     261             :   size_t compile_zone_size =
     262        3547 :       compilation_info()->zone()->allocation_size() - compile_zone_start;
     263        3547 :   size_t translate_zone_size = translate_zone.allocation_size();
     264             :   compilation_info()
     265             :       ->isolate()
     266             :       ->counters()
     267             :       ->asm_wasm_translation_peak_memory_bytes()
     268        7094 :       ->AddSample(static_cast<int>(translate_zone_size));
     269        3547 :   translate_time_ = translate_timer.Elapsed().InMillisecondsF();
     270        3547 :   int module_size = compilation_info()->literal()->end_position() -
     271        3547 :                     compilation_info()->literal()->start_position();
     272             :   compilation_info()->isolate()->counters()->asm_module_size_bytes()->AddSample(
     273        7094 :       module_size);
     274             :   int64_t translation_time_micro = translate_timer.Elapsed().InMicroseconds();
     275             :   // translation_throughput is not exact (assumes MB == 1000000). But that is ok
     276             :   // since the metric is stored in buckets that lose some precision anyways.
     277             :   int translation_throughput =
     278             :       translation_time_micro != 0
     279        3547 :           ? static_cast<int>(static_cast<int64_t>(module_size) /
     280             :                              translation_time_micro)
     281        7094 :           : 0;
     282             :   compilation_info()
     283             :       ->isolate()
     284             :       ->counters()
     285             :       ->asm_wasm_translation_throughput()
     286        7094 :       ->AddSample(translation_throughput);
     287        3547 :   if (FLAG_trace_asm_parser) {
     288             :     PrintF(
     289             :         "[asm.js translation successful: time=%0.3fms, "
     290             :         "translate_zone=%" PRIuS "KB, compile_zone+=%" PRIuS "KB]\n",
     291           0 :         translate_time_, translate_zone_size / KB, compile_zone_size / KB);
     292             :   }
     293             :   return SUCCEEDED;
     294             : }
     295             : 
     296        3547 : CompilationJob::Status AsmJsCompilationJob::FinalizeJobImpl() {
     297             :   // Step 2: Compile and decode the WebAssembly module.
     298             :   base::ElapsedTimer compile_timer;
     299             :   compile_timer.Start();
     300             : 
     301             :   Handle<HeapNumber> uses_bitset =
     302             :       compilation_info()->isolate()->factory()->NewHeapNumberFromBits(
     303       31923 :           stdlib_uses_.ToIntegral());
     304             : 
     305        3547 :   wasm::ErrorThrower thrower(compilation_info()->isolate(), "AsmJs::Compile");
     306             :   Handle<WasmModuleObject> compiled =
     307             :       SyncCompileTranslatedAsmJs(
     308             :           compilation_info()->isolate(), &thrower,
     309             :           wasm::ModuleWireBytes(module_->begin(), module_->end()),
     310             :           parse_info()->script(),
     311       17735 :           Vector<const byte>(asm_offsets_->begin(), asm_offsets_->size()))
     312        7094 :           .ToHandleChecked();
     313             :   DCHECK(!thrower.error());
     314        3547 :   compile_time_ = compile_timer.Elapsed().InMillisecondsF();
     315             : 
     316             :   // The result is a compiled module and serialized standard library uses.
     317             :   Handle<FixedArray> result =
     318             :       compilation_info()->isolate()->factory()->NewFixedArray(
     319        3547 :           kWasmDataEntryCount);
     320        3547 :   result->set(kWasmDataCompiledModule, *compiled);
     321        3547 :   result->set(kWasmDataUsesBitSet, *uses_bitset);
     322             :   compilation_info()->SetAsmWasmData(result);
     323             :   compilation_info()->SetCode(
     324        3547 :       BUILTIN_CODE(compilation_info()->isolate(), InstantiateAsmJs));
     325             : 
     326             :   ReportCompilationSuccess(parse_info()->script(),
     327        3547 :                            compilation_info()->literal()->position(),
     328       14188 :                            translate_time_, compile_time_, module_->size());
     329        3547 :   return SUCCEEDED;
     330             : }
     331             : 
     332      346047 : CompilationJob* AsmJs::NewCompilationJob(ParseInfo* parse_info,
     333             :                                          FunctionLiteral* literal,
     334             :                                          Isolate* isolate) {
     335      346047 :   return new AsmJsCompilationJob(parse_info, literal, isolate);
     336             : }
     337             : 
     338        6365 : MaybeHandle<Object> AsmJs::InstantiateAsmWasm(Isolate* isolate,
     339             :                                               Handle<SharedFunctionInfo> shared,
     340             :                                               Handle<FixedArray> wasm_data,
     341             :                                               Handle<JSReceiver> stdlib,
     342             :                                               Handle<JSReceiver> foreign,
     343             :                                               Handle<JSArrayBuffer> memory) {
     344             :   base::ElapsedTimer instantiate_timer;
     345             :   instantiate_timer.Start();
     346             :   Handle<HeapNumber> uses_bitset(
     347             :       HeapNumber::cast(wasm_data->get(kWasmDataUsesBitSet)));
     348             :   Handle<WasmModuleObject> module(
     349             :       WasmModuleObject::cast(wasm_data->get(kWasmDataCompiledModule)));
     350             :   Handle<Script> script(Script::cast(shared->script()));
     351             :   // TODO(mstarzinger): The position currently points to the module definition
     352             :   // but should instead point to the instantiation site (more intuitive).
     353             :   int position = shared->start_position();
     354             : 
     355             :   // Check that all used stdlib members are valid.
     356        6365 :   bool stdlib_use_of_typed_array_present = false;
     357             :   wasm::AsmJsParser::StdlibSet stdlib_uses(uses_bitset->value_as_bits());
     358        6365 :   if (!stdlib_uses.IsEmpty()) {  // No checking needed if no uses.
     359        2859 :     if (stdlib.is_null()) {
     360          24 :       ReportInstantiationFailure(script, position, "Requires standard library");
     361          24 :       return MaybeHandle<Object>();
     362             :     }
     363        2835 :     if (!AreStdlibMembersValid(isolate, stdlib, stdlib_uses,
     364        2835 :                                &stdlib_use_of_typed_array_present)) {
     365          24 :       ReportInstantiationFailure(script, position, "Unexpected stdlib member");
     366          24 :       return MaybeHandle<Object>();
     367             :     }
     368             :   }
     369             : 
     370             :   // Check that a valid heap buffer is provided if required.
     371        6317 :   if (stdlib_use_of_typed_array_present) {
     372        1670 :     if (memory.is_null()) {
     373          18 :       ReportInstantiationFailure(script, position, "Requires heap buffer");
     374          18 :       return MaybeHandle<Object>();
     375             :     }
     376        1652 :     size_t size = NumberToSize(memory->byte_length());
     377             :     // TODO(mstarzinger): We currently only limit byte length of the buffer to
     378             :     // be a multiple of 8, we should enforce the stricter spec limits here.
     379        1652 :     if (size % FixedTypedArrayBase::kMaxElementSize != 0) {
     380          41 :       ReportInstantiationFailure(script, position, "Unexpected heap size");
     381          41 :       return MaybeHandle<Object>();
     382             :     }
     383             :     // Currently WebAssembly only supports heap sizes within the uint32_t range.
     384        1611 :     if (size > std::numeric_limits<uint32_t>::max()) {
     385           6 :       ReportInstantiationFailure(script, position, "Unexpected heap size");
     386           6 :       return MaybeHandle<Object>();
     387             :     }
     388             :   } else {
     389             :     memory = Handle<JSArrayBuffer>::null();
     390             :   }
     391             : 
     392             :   wasm::ErrorThrower thrower(isolate, "AsmJs::Instantiate");
     393             :   MaybeHandle<Object> maybe_module_object =
     394        6252 :       wasm::SyncInstantiate(isolate, &thrower, module, foreign, memory);
     395        6252 :   if (maybe_module_object.is_null()) {
     396             :     // An exception caused by the module start function will be set as pending
     397             :     // and bypass the {ErrorThrower}, this happens in case of a stack overflow.
     398          69 :     if (isolate->has_pending_exception()) isolate->clear_pending_exception();
     399          69 :     thrower.Reset();  // Ensure exceptions do not propagate.
     400          69 :     ReportInstantiationFailure(script, position, "Internal wasm failure");
     401          69 :     return MaybeHandle<Object>();
     402             :   }
     403             :   DCHECK(!thrower.error());
     404        6183 :   Handle<Object> module_object = maybe_module_object.ToHandleChecked();
     405             : 
     406             :   ReportInstantiationSuccess(script, position,
     407        6183 :                              instantiate_timer.Elapsed().InMillisecondsF());
     408             : 
     409             :   Handle<Name> single_function_name(
     410        6183 :       isolate->factory()->InternalizeUtf8String(AsmJs::kSingleFunctionName));
     411             :   MaybeHandle<Object> single_function =
     412        6183 :       Object::GetProperty(module_object, single_function_name);
     413       12366 :   if (!single_function.is_null() &&
     414             :       !single_function.ToHandleChecked()->IsUndefined(isolate)) {
     415         260 :     return single_function;
     416             :   }
     417             : 
     418             :   Handle<String> exports_name =
     419        5923 :       isolate->factory()->InternalizeUtf8String("exports");
     420        5923 :   return Object::GetProperty(module_object, exports_name);
     421             : }
     422             : 
     423             : }  // namespace internal
     424             : }  // namespace v8

Generated by: LCOV version 1.10