Home | History | Annotate | Download | only in runtime
      1 // Copyright 2014 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/runtime/runtime-utils.h"
      6 
      7 #include "src/arguments.h"
      8 #include "src/asmjs/asm-js.h"
      9 #include "src/compiler-dispatcher/optimizing-compile-dispatcher.h"
     10 #include "src/compiler.h"
     11 #include "src/deoptimizer.h"
     12 #include "src/frames-inl.h"
     13 #include "src/full-codegen/full-codegen.h"
     14 #include "src/isolate-inl.h"
     15 #include "src/messages.h"
     16 #include "src/v8threads.h"
     17 #include "src/vm-state-inl.h"
     18 
     19 namespace v8 {
     20 namespace internal {
     21 
     22 RUNTIME_FUNCTION(Runtime_CompileLazy) {
     23   HandleScope scope(isolate);
     24   DCHECK_EQ(1, args.length());
     25   CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
     26 
     27 #ifdef DEBUG
     28   if (FLAG_trace_lazy && !function->shared()->is_compiled()) {
     29     PrintF("[unoptimized: ");
     30     function->PrintName();
     31     PrintF("]\n");
     32   }
     33 #endif
     34 
     35   StackLimitCheck check(isolate);
     36   if (check.JsHasOverflowed(1 * KB)) return isolate->StackOverflow();
     37   if (!Compiler::Compile(function, Compiler::KEEP_EXCEPTION)) {
     38     return isolate->heap()->exception();
     39   }
     40   DCHECK(function->is_compiled());
     41   return function->code();
     42 }
     43 
     44 RUNTIME_FUNCTION(Runtime_CompileBaseline) {
     45   HandleScope scope(isolate);
     46   DCHECK_EQ(1, args.length());
     47   CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
     48   StackLimitCheck check(isolate);
     49   if (check.JsHasOverflowed(1 * KB)) return isolate->StackOverflow();
     50   if (!Compiler::CompileBaseline(function)) {
     51     return isolate->heap()->exception();
     52   }
     53   DCHECK(function->is_compiled());
     54   return function->code();
     55 }
     56 
     57 RUNTIME_FUNCTION(Runtime_CompileOptimized_Concurrent) {
     58   HandleScope scope(isolate);
     59   DCHECK_EQ(1, args.length());
     60   CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
     61   StackLimitCheck check(isolate);
     62   if (check.JsHasOverflowed(1 * KB)) return isolate->StackOverflow();
     63   if (!Compiler::CompileOptimized(function, Compiler::CONCURRENT)) {
     64     return isolate->heap()->exception();
     65   }
     66   DCHECK(function->is_compiled());
     67   return function->code();
     68 }
     69 
     70 
     71 RUNTIME_FUNCTION(Runtime_CompileOptimized_NotConcurrent) {
     72   HandleScope scope(isolate);
     73   DCHECK_EQ(1, args.length());
     74   CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
     75   StackLimitCheck check(isolate);
     76   if (check.JsHasOverflowed(1 * KB)) return isolate->StackOverflow();
     77   if (!Compiler::CompileOptimized(function, Compiler::NOT_CONCURRENT)) {
     78     return isolate->heap()->exception();
     79   }
     80   DCHECK(function->is_compiled());
     81   return function->code();
     82 }
     83 
     84 RUNTIME_FUNCTION(Runtime_InstantiateAsmJs) {
     85   HandleScope scope(isolate);
     86   DCHECK_EQ(args.length(), 4);
     87   CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
     88 
     89   Handle<JSReceiver> stdlib;
     90   if (args[1]->IsJSReceiver()) {
     91     stdlib = args.at<JSReceiver>(1);
     92   }
     93   Handle<JSObject> foreign;
     94   if (args[2]->IsJSObject()) {
     95     foreign = args.at<JSObject>(2);
     96   }
     97   Handle<JSArrayBuffer> memory;
     98   if (args[3]->IsJSArrayBuffer()) {
     99     memory = args.at<JSArrayBuffer>(3);
    100   }
    101   if (function->shared()->HasAsmWasmData() &&
    102       AsmJs::IsStdlibValid(isolate, handle(function->shared()->asm_wasm_data()),
    103                            stdlib)) {
    104     MaybeHandle<Object> result;
    105     result = AsmJs::InstantiateAsmWasm(
    106         isolate, handle(function->shared()->asm_wasm_data()), memory, foreign);
    107     if (!result.is_null()) {
    108       return *result.ToHandleChecked();
    109     }
    110   }
    111   // Remove wasm data, mark as broken for asm->wasm,
    112   // replace code with CompileLazy, and return a smi 0 to indicate failure.
    113   if (function->shared()->HasAsmWasmData()) {
    114     function->shared()->ClearAsmWasmData();
    115   }
    116   function->shared()->set_is_asm_wasm_broken(true);
    117   DCHECK(function->code() ==
    118          isolate->builtins()->builtin(Builtins::kInstantiateAsmJs));
    119   function->ReplaceCode(isolate->builtins()->builtin(Builtins::kCompileLazy));
    120   if (function->shared()->code() ==
    121       isolate->builtins()->builtin(Builtins::kInstantiateAsmJs)) {
    122     function->shared()->ReplaceCode(
    123         isolate->builtins()->builtin(Builtins::kCompileLazy));
    124   }
    125   return Smi::kZero;
    126 }
    127 
    128 RUNTIME_FUNCTION(Runtime_NotifyStubFailure) {
    129   HandleScope scope(isolate);
    130   DCHECK_EQ(0, args.length());
    131   Deoptimizer* deoptimizer = Deoptimizer::Grab(isolate);
    132   DCHECK(AllowHeapAllocation::IsAllowed());
    133   delete deoptimizer;
    134   return isolate->heap()->undefined_value();
    135 }
    136 
    137 class ActivationsFinder : public ThreadVisitor {
    138  public:
    139   Code* code_;
    140   bool has_code_activations_;
    141 
    142   explicit ActivationsFinder(Code* code)
    143       : code_(code), has_code_activations_(false) {}
    144 
    145   void VisitThread(Isolate* isolate, ThreadLocalTop* top) {
    146     JavaScriptFrameIterator it(isolate, top);
    147     VisitFrames(&it);
    148   }
    149 
    150   void VisitFrames(JavaScriptFrameIterator* it) {
    151     for (; !it->done(); it->Advance()) {
    152       JavaScriptFrame* frame = it->frame();
    153       if (code_->contains(frame->pc())) has_code_activations_ = true;
    154     }
    155   }
    156 };
    157 
    158 
    159 RUNTIME_FUNCTION(Runtime_NotifyDeoptimized) {
    160   HandleScope scope(isolate);
    161   DCHECK_EQ(1, args.length());
    162   CONVERT_SMI_ARG_CHECKED(type_arg, 0);
    163   Deoptimizer::BailoutType type =
    164       static_cast<Deoptimizer::BailoutType>(type_arg);
    165   Deoptimizer* deoptimizer = Deoptimizer::Grab(isolate);
    166   DCHECK(AllowHeapAllocation::IsAllowed());
    167   TimerEventScope<TimerEventDeoptimizeCode> timer(isolate);
    168   TRACE_EVENT0("v8", "V8.DeoptimizeCode");
    169 
    170   Handle<JSFunction> function = deoptimizer->function();
    171   Handle<Code> optimized_code = deoptimizer->compiled_code();
    172 
    173   DCHECK(optimized_code->kind() == Code::OPTIMIZED_FUNCTION);
    174   DCHECK(type == deoptimizer->bailout_type());
    175   DCHECK_NULL(isolate->context());
    176 
    177   // TODO(turbofan): For Crankshaft we restore the context before objects are
    178   // being materialized, because it never de-materializes the context but it
    179   // requires a context to materialize arguments objects. This is specific to
    180   // Crankshaft and can be removed once only TurboFan goes through here.
    181   if (!optimized_code->is_turbofanned()) {
    182     JavaScriptFrameIterator top_it(isolate);
    183     JavaScriptFrame* top_frame = top_it.frame();
    184     isolate->set_context(Context::cast(top_frame->context()));
    185   } else {
    186     // TODO(turbofan): We currently need the native context to materialize
    187     // the arguments object, but only to get to its map.
    188     isolate->set_context(function->native_context());
    189   }
    190 
    191   // Make sure to materialize objects before causing any allocation.
    192   JavaScriptFrameIterator it(isolate);
    193   deoptimizer->MaterializeHeapObjects(&it);
    194   delete deoptimizer;
    195 
    196   // Ensure the context register is updated for materialized objects.
    197   if (optimized_code->is_turbofanned()) {
    198     JavaScriptFrameIterator top_it(isolate);
    199     JavaScriptFrame* top_frame = top_it.frame();
    200     isolate->set_context(Context::cast(top_frame->context()));
    201   }
    202 
    203   if (type == Deoptimizer::LAZY) {
    204     return isolate->heap()->undefined_value();
    205   }
    206 
    207   // Search for other activations of the same optimized code.
    208   // At this point {it} is at the topmost frame of all the frames materialized
    209   // by the deoptimizer. Note that this frame does not necessarily represent
    210   // an activation of {function} because of potential inlined tail-calls.
    211   ActivationsFinder activations_finder(*optimized_code);
    212   activations_finder.VisitFrames(&it);
    213   isolate->thread_manager()->IterateArchivedThreads(&activations_finder);
    214 
    215   if (!activations_finder.has_code_activations_) {
    216     if (function->code() == *optimized_code) {
    217       if (FLAG_trace_deopt) {
    218         PrintF("[removing optimized code for: ");
    219         function->PrintName();
    220         PrintF("]\n");
    221       }
    222       function->ReplaceCode(function->shared()->code());
    223     }
    224     // Evict optimized code for this function from the cache so that it
    225     // doesn't get used for new closures.
    226     function->shared()->EvictFromOptimizedCodeMap(*optimized_code,
    227                                                   "notify deoptimized");
    228   } else {
    229     // TODO(titzer): we should probably do DeoptimizeCodeList(code)
    230     // unconditionally if the code is not already marked for deoptimization.
    231     // If there is an index by shared function info, all the better.
    232     Deoptimizer::DeoptimizeFunction(*function);
    233   }
    234 
    235   return isolate->heap()->undefined_value();
    236 }
    237 
    238 
    239 static bool IsSuitableForOnStackReplacement(Isolate* isolate,
    240                                             Handle<JSFunction> function) {
    241   // Keep track of whether we've succeeded in optimizing.
    242   if (function->shared()->optimization_disabled()) return false;
    243   // If we are trying to do OSR when there are already optimized
    244   // activations of the function, it means (a) the function is directly or
    245   // indirectly recursive and (b) an optimized invocation has been
    246   // deoptimized so that we are currently in an unoptimized activation.
    247   // Check for optimized activations of this function.
    248   for (JavaScriptFrameIterator it(isolate); !it.done(); it.Advance()) {
    249     JavaScriptFrame* frame = it.frame();
    250     if (frame->is_optimized() && frame->function() == *function) return false;
    251   }
    252 
    253   return true;
    254 }
    255 
    256 namespace {
    257 
    258 BailoutId DetermineEntryAndDisarmOSRForBaseline(JavaScriptFrame* frame) {
    259   Handle<Code> caller_code(frame->function()->shared()->code());
    260 
    261   // Passing the PC in the JavaScript frame from the caller directly is
    262   // not GC safe, so we walk the stack to get it.
    263   if (!caller_code->contains(frame->pc())) {
    264     // Code on the stack may not be the code object referenced by the shared
    265     // function info.  It may have been replaced to include deoptimization data.
    266     caller_code = Handle<Code>(frame->LookupCode());
    267   }
    268 
    269   DCHECK_EQ(frame->LookupCode(), *caller_code);
    270   DCHECK_EQ(Code::FUNCTION, caller_code->kind());
    271   DCHECK(caller_code->contains(frame->pc()));
    272 
    273   // Revert the patched back edge table, regardless of whether OSR succeeds.
    274   BackEdgeTable::Revert(frame->isolate(), *caller_code);
    275 
    276   // Return a BailoutId representing an AST id of the {IterationStatement}.
    277   uint32_t pc_offset =
    278       static_cast<uint32_t>(frame->pc() - caller_code->instruction_start());
    279   return caller_code->TranslatePcOffsetToAstId(pc_offset);
    280 }
    281 
    282 BailoutId DetermineEntryAndDisarmOSRForInterpreter(JavaScriptFrame* frame) {
    283   InterpretedFrame* iframe = reinterpret_cast<InterpretedFrame*>(frame);
    284 
    285   // Note that the bytecode array active on the stack might be different from
    286   // the one installed on the function (e.g. patched by debugger). This however
    287   // is fine because we guarantee the layout to be in sync, hence any BailoutId
    288   // representing the entry point will be valid for any copy of the bytecode.
    289   Handle<BytecodeArray> bytecode(iframe->GetBytecodeArray());
    290 
    291   DCHECK(frame->LookupCode()->is_interpreter_trampoline_builtin());
    292   DCHECK(frame->function()->shared()->HasBytecodeArray());
    293   DCHECK(frame->is_interpreted());
    294   DCHECK(FLAG_ignition_osr);
    295 
    296   // Reset the OSR loop nesting depth to disarm back edges.
    297   bytecode->set_osr_loop_nesting_level(0);
    298 
    299   // Return a BailoutId representing the bytecode offset of the back branch.
    300   return BailoutId(iframe->GetBytecodeOffset());
    301 }
    302 
    303 }  // namespace
    304 
    305 RUNTIME_FUNCTION(Runtime_CompileForOnStackReplacement) {
    306   HandleScope scope(isolate);
    307   DCHECK_EQ(1, args.length());
    308   CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
    309 
    310   // We're not prepared to handle a function with arguments object.
    311   DCHECK(!function->shared()->uses_arguments());
    312 
    313   // Only reachable when OST is enabled.
    314   CHECK(FLAG_use_osr);
    315 
    316   // Determine frame triggering OSR request.
    317   JavaScriptFrameIterator it(isolate);
    318   JavaScriptFrame* frame = it.frame();
    319   DCHECK_EQ(frame->function(), *function);
    320 
    321   // Determine the entry point for which this OSR request has been fired and
    322   // also disarm all back edges in the calling code to stop new requests.
    323   BailoutId ast_id = frame->is_interpreted()
    324                          ? DetermineEntryAndDisarmOSRForInterpreter(frame)
    325                          : DetermineEntryAndDisarmOSRForBaseline(frame);
    326   DCHECK(!ast_id.IsNone());
    327 
    328   MaybeHandle<Code> maybe_result;
    329   if (IsSuitableForOnStackReplacement(isolate, function)) {
    330     if (FLAG_trace_osr) {
    331       PrintF("[OSR - Compiling: ");
    332       function->PrintName();
    333       PrintF(" at AST id %d]\n", ast_id.ToInt());
    334     }
    335     maybe_result = Compiler::GetOptimizedCodeForOSR(function, ast_id, frame);
    336   }
    337 
    338   // Check whether we ended up with usable optimized code.
    339   Handle<Code> result;
    340   if (maybe_result.ToHandle(&result) &&
    341       result->kind() == Code::OPTIMIZED_FUNCTION) {
    342     DeoptimizationInputData* data =
    343         DeoptimizationInputData::cast(result->deoptimization_data());
    344 
    345     if (data->OsrPcOffset()->value() >= 0) {
    346       DCHECK(BailoutId(data->OsrAstId()->value()) == ast_id);
    347       if (FLAG_trace_osr) {
    348         PrintF("[OSR - Entry at AST id %d, offset %d in optimized code]\n",
    349                ast_id.ToInt(), data->OsrPcOffset()->value());
    350       }
    351       // TODO(titzer): this is a massive hack to make the deopt counts
    352       // match. Fix heuristics for reenabling optimizations!
    353       function->shared()->increment_deopt_count();
    354 
    355       if (result->is_turbofanned()) {
    356         // When we're waiting for concurrent optimization, set to compile on
    357         // the next call - otherwise we'd run unoptimized once more
    358         // and potentially compile for OSR another time as well.
    359         if (function->IsMarkedForConcurrentOptimization()) {
    360           if (FLAG_trace_osr) {
    361             PrintF("[OSR - Re-marking ");
    362             function->PrintName();
    363             PrintF(" for non-concurrent optimization]\n");
    364           }
    365           function->ReplaceCode(
    366               isolate->builtins()->builtin(Builtins::kCompileOptimized));
    367         }
    368       } else {
    369         // Crankshafted OSR code can be installed into the function.
    370         function->ReplaceCode(*result);
    371       }
    372       return *result;
    373     }
    374   }
    375 
    376   // Failed.
    377   if (FLAG_trace_osr) {
    378     PrintF("[OSR - Failed: ");
    379     function->PrintName();
    380     PrintF(" at AST id %d]\n", ast_id.ToInt());
    381   }
    382 
    383   if (!function->IsOptimized()) {
    384     function->ReplaceCode(function->shared()->code());
    385   }
    386   return NULL;
    387 }
    388 
    389 
    390 RUNTIME_FUNCTION(Runtime_TryInstallOptimizedCode) {
    391   HandleScope scope(isolate);
    392   DCHECK_EQ(1, args.length());
    393   CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
    394 
    395   // First check if this is a real stack overflow.
    396   StackLimitCheck check(isolate);
    397   if (check.JsHasOverflowed()) {
    398     SealHandleScope shs(isolate);
    399     return isolate->StackOverflow();
    400   }
    401 
    402   isolate->optimizing_compile_dispatcher()->InstallOptimizedFunctions();
    403   return (function->IsOptimized()) ? function->code()
    404                                    : function->shared()->code();
    405 }
    406 
    407 
    408 bool CodeGenerationFromStringsAllowed(Isolate* isolate,
    409                                       Handle<Context> context) {
    410   DCHECK(context->allow_code_gen_from_strings()->IsFalse(isolate));
    411   // Check with callback if set.
    412   AllowCodeGenerationFromStringsCallback callback =
    413       isolate->allow_code_gen_callback();
    414   if (callback == NULL) {
    415     // No callback set and code generation disallowed.
    416     return false;
    417   } else {
    418     // Callback set. Let it decide if code generation is allowed.
    419     VMState<EXTERNAL> state(isolate);
    420     return callback(v8::Utils::ToLocal(context));
    421   }
    422 }
    423 
    424 static Object* CompileGlobalEval(Isolate* isolate, Handle<String> source,
    425                                  Handle<SharedFunctionInfo> outer_info,
    426                                  LanguageMode language_mode,
    427                                  int eval_scope_position, int eval_position) {
    428   Handle<Context> context = Handle<Context>(isolate->context());
    429   Handle<Context> native_context = Handle<Context>(context->native_context());
    430 
    431   // Check if native context allows code generation from
    432   // strings. Throw an exception if it doesn't.
    433   if (native_context->allow_code_gen_from_strings()->IsFalse(isolate) &&
    434       !CodeGenerationFromStringsAllowed(isolate, native_context)) {
    435     Handle<Object> error_message =
    436         native_context->ErrorMessageForCodeGenerationFromStrings();
    437     Handle<Object> error;
    438     MaybeHandle<Object> maybe_error = isolate->factory()->NewEvalError(
    439         MessageTemplate::kCodeGenFromStrings, error_message);
    440     if (maybe_error.ToHandle(&error)) isolate->Throw(*error);
    441     return isolate->heap()->exception();
    442   }
    443 
    444   // Deal with a normal eval call with a string argument. Compile it
    445   // and return the compiled function bound in the local context.
    446   static const ParseRestriction restriction = NO_PARSE_RESTRICTION;
    447   Handle<JSFunction> compiled;
    448   ASSIGN_RETURN_ON_EXCEPTION_VALUE(
    449       isolate, compiled,
    450       Compiler::GetFunctionFromEval(source, outer_info, context, language_mode,
    451                                     restriction, kNoSourcePosition,
    452                                     eval_scope_position, eval_position),
    453       isolate->heap()->exception());
    454   return *compiled;
    455 }
    456 
    457 
    458 RUNTIME_FUNCTION(Runtime_ResolvePossiblyDirectEval) {
    459   HandleScope scope(isolate);
    460   DCHECK_EQ(6, args.length());
    461 
    462   Handle<Object> callee = args.at(0);
    463 
    464   // If "eval" didn't refer to the original GlobalEval, it's not a
    465   // direct call to eval.
    466   // (And even if it is, but the first argument isn't a string, just let
    467   // execution default to an indirect call to eval, which will also return
    468   // the first argument without doing anything).
    469   if (*callee != isolate->native_context()->global_eval_fun() ||
    470       !args[1]->IsString()) {
    471     return *callee;
    472   }
    473 
    474   DCHECK(args[3]->IsSmi());
    475   DCHECK(is_valid_language_mode(args.smi_at(3)));
    476   LanguageMode language_mode = static_cast<LanguageMode>(args.smi_at(3));
    477   DCHECK(args[4]->IsSmi());
    478   Handle<SharedFunctionInfo> outer_info(args.at<JSFunction>(2)->shared(),
    479                                         isolate);
    480   return CompileGlobalEval(isolate, args.at<String>(1), outer_info,
    481                            language_mode, args.smi_at(4), args.smi_at(5));
    482 }
    483 }  // namespace internal
    484 }  // namespace v8
    485