Home | History | Annotate | Download | only in compiler-dispatcher
      1 // Copyright 2016 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/compiler-dispatcher/compiler-dispatcher-job.h"
      6 
      7 #include "src/assert-scope.h"
      8 #include "src/compilation-info.h"
      9 #include "src/compiler-dispatcher/compiler-dispatcher-tracer.h"
     10 #include "src/compiler.h"
     11 #include "src/flags.h"
     12 #include "src/global-handles.h"
     13 #include "src/isolate.h"
     14 #include "src/objects-inl.h"
     15 #include "src/parsing/parse-info.h"
     16 #include "src/parsing/parser.h"
     17 #include "src/parsing/scanner-character-streams.h"
     18 #include "src/unicode-cache.h"
     19 #include "src/utils.h"
     20 
     21 namespace v8 {
     22 namespace internal {
     23 
     24 namespace {
     25 
     26 class OneByteWrapper : public v8::String::ExternalOneByteStringResource {
     27  public:
     28   OneByteWrapper(const void* data, int length) : data_(data), length_(length) {}
     29   ~OneByteWrapper() override = default;
     30 
     31   const char* data() const override {
     32     return reinterpret_cast<const char*>(data_);
     33   }
     34 
     35   size_t length() const override { return static_cast<size_t>(length_); }
     36 
     37  private:
     38   const void* data_;
     39   int length_;
     40 
     41   DISALLOW_COPY_AND_ASSIGN(OneByteWrapper);
     42 };
     43 
     44 class TwoByteWrapper : public v8::String::ExternalStringResource {
     45  public:
     46   TwoByteWrapper(const void* data, int length) : data_(data), length_(length) {}
     47   ~TwoByteWrapper() override = default;
     48 
     49   const uint16_t* data() const override {
     50     return reinterpret_cast<const uint16_t*>(data_);
     51   }
     52 
     53   size_t length() const override { return static_cast<size_t>(length_); }
     54 
     55  private:
     56   const void* data_;
     57   int length_;
     58 
     59   DISALLOW_COPY_AND_ASSIGN(TwoByteWrapper);
     60 };
     61 
     62 }  // namespace
     63 
     64 CompilerDispatcherJob::CompilerDispatcherJob(Isolate* isolate,
     65                                              CompilerDispatcherTracer* tracer,
     66                                              Handle<SharedFunctionInfo> shared,
     67                                              size_t max_stack_size)
     68     : status_(CompileJobStatus::kInitial),
     69       isolate_(isolate),
     70       tracer_(tracer),
     71       context_(Handle<Context>::cast(
     72           isolate_->global_handles()->Create(isolate->context()))),
     73       shared_(Handle<SharedFunctionInfo>::cast(
     74           isolate_->global_handles()->Create(*shared))),
     75       max_stack_size_(max_stack_size),
     76       trace_compiler_dispatcher_jobs_(FLAG_trace_compiler_dispatcher_jobs) {
     77   DCHECK(!shared_->is_toplevel());
     78   HandleScope scope(isolate_);
     79   Handle<Script> script(Script::cast(shared_->script()), isolate_);
     80   Handle<String> source(String::cast(script->source()), isolate_);
     81   if (trace_compiler_dispatcher_jobs_) {
     82     PrintF("CompilerDispatcherJob[%p] created for ", static_cast<void*>(this));
     83     shared_->ShortPrint();
     84     PrintF(" in initial state.\n");
     85   }
     86 }
     87 
     88 CompilerDispatcherJob::CompilerDispatcherJob(
     89     Isolate* isolate, CompilerDispatcherTracer* tracer, Handle<Script> script,
     90     Handle<SharedFunctionInfo> shared, FunctionLiteral* literal,
     91     std::shared_ptr<Zone> parse_zone,
     92     std::shared_ptr<DeferredHandles> parse_handles,
     93     std::shared_ptr<DeferredHandles> compile_handles, size_t max_stack_size)
     94     : status_(CompileJobStatus::kAnalyzed),
     95       isolate_(isolate),
     96       tracer_(tracer),
     97       context_(Handle<Context>::cast(
     98           isolate_->global_handles()->Create(isolate->context()))),
     99       shared_(Handle<SharedFunctionInfo>::cast(
    100           isolate_->global_handles()->Create(*shared))),
    101       max_stack_size_(max_stack_size),
    102       parse_info_(new ParseInfo(shared_)),
    103       parse_zone_(parse_zone),
    104       compile_info_(new CompilationInfo(parse_info_->zone(), parse_info_.get(),
    105                                         Handle<JSFunction>::null())),
    106       trace_compiler_dispatcher_jobs_(FLAG_trace_compiler_dispatcher_jobs) {
    107   parse_info_->set_literal(literal);
    108   parse_info_->set_script(script);
    109   parse_info_->set_deferred_handles(parse_handles);
    110   compile_info_->set_deferred_handles(compile_handles);
    111 
    112   if (trace_compiler_dispatcher_jobs_) {
    113     PrintF("CompilerDispatcherJob[%p] created for ", static_cast<void*>(this));
    114     shared_->ShortPrint();
    115     PrintF(" in Analyzed state.\n");
    116   }
    117 }
    118 
    119 CompilerDispatcherJob::~CompilerDispatcherJob() {
    120   DCHECK(ThreadId::Current().Equals(isolate_->thread_id()));
    121   DCHECK(status_ == CompileJobStatus::kInitial ||
    122          status_ == CompileJobStatus::kDone);
    123   i::GlobalHandles::Destroy(Handle<Object>::cast(shared_).location());
    124   i::GlobalHandles::Destroy(Handle<Object>::cast(context_).location());
    125 }
    126 
    127 bool CompilerDispatcherJob::IsAssociatedWith(
    128     Handle<SharedFunctionInfo> shared) const {
    129   return *shared_ == *shared;
    130 }
    131 
    132 void CompilerDispatcherJob::PrepareToParseOnMainThread() {
    133   DCHECK(ThreadId::Current().Equals(isolate_->thread_id()));
    134   DCHECK(status() == CompileJobStatus::kInitial);
    135   COMPILER_DISPATCHER_TRACE_SCOPE(tracer_, kPrepareToParse);
    136   if (trace_compiler_dispatcher_jobs_) {
    137     PrintF("CompilerDispatcherJob[%p]: Preparing to parse\n",
    138            static_cast<void*>(this));
    139   }
    140   HandleScope scope(isolate_);
    141   unicode_cache_.reset(new UnicodeCache());
    142   Handle<Script> script(Script::cast(shared_->script()), isolate_);
    143   DCHECK(script->type() != Script::TYPE_NATIVE);
    144 
    145   Handle<String> source(String::cast(script->source()), isolate_);
    146   parse_info_.reset(new ParseInfo(isolate_->allocator()));
    147   if (source->IsExternalTwoByteString() || source->IsExternalOneByteString()) {
    148     character_stream_.reset(ScannerStream::For(
    149         source, shared_->start_position(), shared_->end_position()));
    150   } else {
    151     source = String::Flatten(source);
    152     const void* data;
    153     int offset = 0;
    154     int length = source->length();
    155 
    156     // Objects in lo_space don't move, so we can just read the contents from
    157     // any thread.
    158     if (isolate_->heap()->lo_space()->Contains(*source)) {
    159       // We need to globalize the handle to the flattened string here, in
    160       // case it's not referenced from anywhere else.
    161       source_ =
    162           Handle<String>::cast(isolate_->global_handles()->Create(*source));
    163       DisallowHeapAllocation no_allocation;
    164       String::FlatContent content = source->GetFlatContent();
    165       DCHECK(content.IsFlat());
    166       data =
    167           content.IsOneByte()
    168               ? reinterpret_cast<const void*>(content.ToOneByteVector().start())
    169               : reinterpret_cast<const void*>(content.ToUC16Vector().start());
    170     } else {
    171       // Otherwise, create a copy of the part of the string we'll parse in the
    172       // zone.
    173       length = (shared_->end_position() - shared_->start_position());
    174       offset = shared_->start_position();
    175 
    176       int byte_len = length * (source->IsOneByteRepresentation() ? 1 : 2);
    177       data = parse_info_->zone()->New(byte_len);
    178 
    179       DisallowHeapAllocation no_allocation;
    180       String::FlatContent content = source->GetFlatContent();
    181       DCHECK(content.IsFlat());
    182       if (content.IsOneByte()) {
    183         MemCopy(const_cast<void*>(data),
    184                 &content.ToOneByteVector().at(shared_->start_position()),
    185                 byte_len);
    186       } else {
    187         MemCopy(const_cast<void*>(data),
    188                 &content.ToUC16Vector().at(shared_->start_position()),
    189                 byte_len);
    190       }
    191     }
    192     Handle<String> wrapper;
    193     if (source->IsOneByteRepresentation()) {
    194       ExternalOneByteString::Resource* resource =
    195           new OneByteWrapper(data, length);
    196       source_wrapper_.reset(resource);
    197       wrapper = isolate_->factory()
    198                     ->NewExternalStringFromOneByte(resource)
    199                     .ToHandleChecked();
    200     } else {
    201       ExternalTwoByteString::Resource* resource =
    202           new TwoByteWrapper(data, length);
    203       source_wrapper_.reset(resource);
    204       wrapper = isolate_->factory()
    205                     ->NewExternalStringFromTwoByte(resource)
    206                     .ToHandleChecked();
    207     }
    208     wrapper_ =
    209         Handle<String>::cast(isolate_->global_handles()->Create(*wrapper));
    210 
    211     character_stream_.reset(
    212         ScannerStream::For(wrapper_, shared_->start_position() - offset,
    213                            shared_->end_position() - offset));
    214   }
    215   parse_info_->set_isolate(isolate_);
    216   parse_info_->set_character_stream(character_stream_.get());
    217   parse_info_->set_hash_seed(isolate_->heap()->HashSeed());
    218   parse_info_->set_is_named_expression(shared_->is_named_expression());
    219   parse_info_->set_compiler_hints(shared_->compiler_hints());
    220   parse_info_->set_start_position(shared_->start_position());
    221   parse_info_->set_end_position(shared_->end_position());
    222   parse_info_->set_unicode_cache(unicode_cache_.get());
    223   parse_info_->set_language_mode(shared_->language_mode());
    224   parse_info_->set_function_literal_id(shared_->function_literal_id());
    225 
    226   parser_.reset(new Parser(parse_info_.get()));
    227   MaybeHandle<ScopeInfo> outer_scope_info;
    228   if (!shared_->outer_scope_info()->IsTheHole(isolate_) &&
    229       ScopeInfo::cast(shared_->outer_scope_info())->length() > 0) {
    230     outer_scope_info = handle(ScopeInfo::cast(shared_->outer_scope_info()));
    231   }
    232   parser_->DeserializeScopeChain(parse_info_.get(), outer_scope_info);
    233 
    234   Handle<String> name(String::cast(shared_->name()));
    235   parse_info_->set_function_name(
    236       parse_info_->ast_value_factory()->GetString(name));
    237   status_ = CompileJobStatus::kReadyToParse;
    238 }
    239 
    240 void CompilerDispatcherJob::Parse() {
    241   DCHECK(status() == CompileJobStatus::kReadyToParse);
    242   COMPILER_DISPATCHER_TRACE_SCOPE_WITH_NUM(
    243       tracer_, kParse,
    244       parse_info_->end_position() - parse_info_->start_position());
    245   if (trace_compiler_dispatcher_jobs_) {
    246     PrintF("CompilerDispatcherJob[%p]: Parsing\n", static_cast<void*>(this));
    247   }
    248 
    249   DisallowHeapAllocation no_allocation;
    250   DisallowHandleAllocation no_handles;
    251   DisallowHandleDereference no_deref;
    252 
    253   // Nullify the Isolate temporarily so that the parser doesn't accidentally
    254   // use it.
    255   parse_info_->set_isolate(nullptr);
    256 
    257   uintptr_t stack_limit = GetCurrentStackPosition() - max_stack_size_ * KB;
    258 
    259   parser_->set_stack_limit(stack_limit);
    260   parser_->ParseOnBackground(parse_info_.get());
    261 
    262   parse_info_->set_isolate(isolate_);
    263 
    264   status_ = CompileJobStatus::kParsed;
    265 }
    266 
    267 bool CompilerDispatcherJob::FinalizeParsingOnMainThread() {
    268   DCHECK(ThreadId::Current().Equals(isolate_->thread_id()));
    269   DCHECK(status() == CompileJobStatus::kParsed);
    270   COMPILER_DISPATCHER_TRACE_SCOPE(tracer_, kFinalizeParsing);
    271   if (trace_compiler_dispatcher_jobs_) {
    272     PrintF("CompilerDispatcherJob[%p]: Finalizing parsing\n",
    273            static_cast<void*>(this));
    274   }
    275 
    276   if (!source_.is_null()) {
    277     i::GlobalHandles::Destroy(Handle<Object>::cast(source_).location());
    278     source_ = Handle<String>::null();
    279   }
    280   if (!wrapper_.is_null()) {
    281     i::GlobalHandles::Destroy(Handle<Object>::cast(wrapper_).location());
    282     wrapper_ = Handle<String>::null();
    283   }
    284 
    285   Handle<Script> script(Script::cast(shared_->script()), isolate_);
    286   parse_info_->set_script(script);
    287   if (parse_info_->literal() == nullptr) {
    288     parser_->ReportErrors(isolate_, script);
    289     status_ = CompileJobStatus::kFailed;
    290   } else {
    291     status_ = CompileJobStatus::kReadyToAnalyze;
    292   }
    293   parser_->UpdateStatistics(isolate_, script);
    294 
    295   DeferredHandleScope scope(isolate_);
    296   {
    297     parse_info_->ReopenHandlesInNewHandleScope();
    298 
    299     if (!shared_->outer_scope_info()->IsTheHole(isolate_) &&
    300         ScopeInfo::cast(shared_->outer_scope_info())->length() > 0) {
    301       Handle<ScopeInfo> outer_scope_info(
    302           handle(ScopeInfo::cast(shared_->outer_scope_info())));
    303       parse_info_->set_outer_scope_info(outer_scope_info);
    304     }
    305     parse_info_->set_shared_info(shared_);
    306 
    307     // Internalize ast values on the main thread.
    308     parse_info_->ast_value_factory()->Internalize(isolate_);
    309     parser_->HandleSourceURLComments(isolate_, script);
    310 
    311     parse_info_->set_character_stream(nullptr);
    312     parse_info_->set_unicode_cache(nullptr);
    313     parser_.reset();
    314     unicode_cache_.reset();
    315     character_stream_.reset();
    316   }
    317   parse_info_->set_deferred_handles(scope.Detach());
    318 
    319   return status_ != CompileJobStatus::kFailed;
    320 }
    321 
    322 bool CompilerDispatcherJob::AnalyzeOnMainThread() {
    323   DCHECK(ThreadId::Current().Equals(isolate_->thread_id()));
    324   DCHECK(status() == CompileJobStatus::kReadyToAnalyze);
    325   COMPILER_DISPATCHER_TRACE_SCOPE(tracer_, kAnalyze);
    326   if (trace_compiler_dispatcher_jobs_) {
    327     PrintF("CompilerDispatcherJob[%p]: Analyzing\n", static_cast<void*>(this));
    328   }
    329 
    330   compile_info_.reset(new CompilationInfo(
    331       parse_info_->zone(), parse_info_.get(), Handle<JSFunction>::null()));
    332 
    333   DeferredHandleScope scope(isolate_);
    334   {
    335     if (Compiler::Analyze(parse_info_.get())) {
    336       status_ = CompileJobStatus::kAnalyzed;
    337     } else {
    338       status_ = CompileJobStatus::kFailed;
    339       if (!isolate_->has_pending_exception()) isolate_->StackOverflow();
    340     }
    341   }
    342   compile_info_->set_deferred_handles(scope.Detach());
    343 
    344   return status_ != CompileJobStatus::kFailed;
    345 }
    346 
    347 bool CompilerDispatcherJob::PrepareToCompileOnMainThread() {
    348   DCHECK(ThreadId::Current().Equals(isolate_->thread_id()));
    349   DCHECK(status() == CompileJobStatus::kAnalyzed);
    350   COMPILER_DISPATCHER_TRACE_SCOPE(tracer_, kPrepareToCompile);
    351 
    352   compile_job_.reset(
    353       Compiler::PrepareUnoptimizedCompilationJob(compile_info_.get()));
    354   if (!compile_job_.get()) {
    355     if (!isolate_->has_pending_exception()) isolate_->StackOverflow();
    356     status_ = CompileJobStatus::kFailed;
    357     return false;
    358   }
    359 
    360   CHECK(compile_job_->can_execute_on_background_thread());
    361   status_ = CompileJobStatus::kReadyToCompile;
    362   return true;
    363 }
    364 
    365 void CompilerDispatcherJob::Compile() {
    366   DCHECK(status() == CompileJobStatus::kReadyToCompile);
    367   COMPILER_DISPATCHER_TRACE_SCOPE_WITH_NUM(
    368       tracer_, kCompile, parse_info_->literal()->ast_node_count());
    369   if (trace_compiler_dispatcher_jobs_) {
    370     PrintF("CompilerDispatcherJob[%p]: Compiling\n", static_cast<void*>(this));
    371   }
    372 
    373   // Disallowing of handle dereference and heap access dealt with in
    374   // CompilationJob::ExecuteJob.
    375 
    376   uintptr_t stack_limit = GetCurrentStackPosition() - max_stack_size_ * KB;
    377   compile_job_->set_stack_limit(stack_limit);
    378 
    379   CompilationJob::Status status = compile_job_->ExecuteJob();
    380   USE(status);
    381 
    382   // Always transition to kCompiled - errors will be reported by
    383   // FinalizeCompilingOnMainThread.
    384   status_ = CompileJobStatus::kCompiled;
    385 }
    386 
    387 bool CompilerDispatcherJob::FinalizeCompilingOnMainThread() {
    388   DCHECK(ThreadId::Current().Equals(isolate_->thread_id()));
    389   DCHECK(status() == CompileJobStatus::kCompiled);
    390   COMPILER_DISPATCHER_TRACE_SCOPE(tracer_, kFinalizeCompiling);
    391   if (trace_compiler_dispatcher_jobs_) {
    392     PrintF("CompilerDispatcherJob[%p]: Finalizing compiling\n",
    393            static_cast<void*>(this));
    394   }
    395 
    396   {
    397     HandleScope scope(isolate_);
    398     if (compile_job_->state() == CompilationJob::State::kFailed ||
    399         !Compiler::FinalizeCompilationJob(compile_job_.release())) {
    400       if (!isolate_->has_pending_exception()) isolate_->StackOverflow();
    401       status_ = CompileJobStatus::kFailed;
    402       return false;
    403     }
    404   }
    405 
    406   compile_job_.reset();
    407   compile_info_.reset();
    408   parse_zone_.reset();
    409   parse_info_.reset();
    410 
    411   status_ = CompileJobStatus::kDone;
    412   return true;
    413 }
    414 
    415 void CompilerDispatcherJob::ResetOnMainThread() {
    416   DCHECK(ThreadId::Current().Equals(isolate_->thread_id()));
    417 
    418   if (trace_compiler_dispatcher_jobs_) {
    419     PrintF("CompilerDispatcherJob[%p]: Resetting\n", static_cast<void*>(this));
    420   }
    421 
    422   compile_job_.reset();
    423   compile_info_.reset();
    424   parse_zone_.reset();
    425   parser_.reset();
    426   unicode_cache_.reset();
    427   character_stream_.reset();
    428   parse_info_.reset();
    429 
    430   if (!source_.is_null()) {
    431     i::GlobalHandles::Destroy(Handle<Object>::cast(source_).location());
    432     source_ = Handle<String>::null();
    433   }
    434   if (!wrapper_.is_null()) {
    435     i::GlobalHandles::Destroy(Handle<Object>::cast(wrapper_).location());
    436     wrapper_ = Handle<String>::null();
    437   }
    438 
    439   status_ = CompileJobStatus::kInitial;
    440 }
    441 
    442 double CompilerDispatcherJob::EstimateRuntimeOfNextStepInMs() const {
    443   switch (status_) {
    444     case CompileJobStatus::kInitial:
    445       return tracer_->EstimatePrepareToParseInMs();
    446 
    447     case CompileJobStatus::kReadyToParse:
    448       return tracer_->EstimateParseInMs(parse_info_->end_position() -
    449                                         parse_info_->start_position());
    450 
    451     case CompileJobStatus::kParsed:
    452       return tracer_->EstimateFinalizeParsingInMs();
    453 
    454     case CompileJobStatus::kReadyToAnalyze:
    455       return tracer_->EstimateAnalyzeInMs();
    456 
    457     case CompileJobStatus::kAnalyzed:
    458       return tracer_->EstimatePrepareToCompileInMs();
    459 
    460     case CompileJobStatus::kReadyToCompile:
    461       return tracer_->EstimateCompileInMs(
    462           parse_info_->literal()->ast_node_count());
    463 
    464     case CompileJobStatus::kCompiled:
    465       return tracer_->EstimateFinalizeCompilingInMs();
    466 
    467     case CompileJobStatus::kFailed:
    468     case CompileJobStatus::kDone:
    469       return 0.0;
    470   }
    471 
    472   UNREACHABLE();
    473   return 0.0;
    474 }
    475 
    476 void CompilerDispatcherJob::ShortPrint() {
    477   DCHECK(ThreadId::Current().Equals(isolate_->thread_id()));
    478   shared_->ShortPrint();
    479 }
    480 
    481 }  // namespace internal
    482 }  // namespace v8
    483