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/arguments-inl.h"
      6 #include "src/asmjs/asm-js.h"
      7 #include "src/compiler-dispatcher/optimizing-compile-dispatcher.h"
      8 #include "src/compiler.h"
      9 #include "src/deoptimizer.h"
     10 #include "src/frames-inl.h"
     11 #include "src/isolate-inl.h"
     12 #include "src/messages.h"
     13 #include "src/objects/js-array-buffer-inl.h"
     14 #include "src/objects/js-array-inl.h"
     15 #include "src/runtime/runtime-utils.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(kStackSpaceRequiredForCompilation * KB)) {
     37     return isolate->StackOverflow();
     38   }
     39   if (!Compiler::Compile(function, Compiler::KEEP_EXCEPTION)) {
     40     return ReadOnlyRoots(isolate).exception();
     41   }
     42   DCHECK(function->is_compiled());
     43   return function->code();
     44 }
     45 
     46 RUNTIME_FUNCTION(Runtime_CompileOptimized_Concurrent) {
     47   HandleScope scope(isolate);
     48   DCHECK_EQ(1, args.length());
     49   CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
     50   StackLimitCheck check(isolate);
     51   if (check.JsHasOverflowed(kStackSpaceRequiredForCompilation * KB)) {
     52     return isolate->StackOverflow();
     53   }
     54   if (!Compiler::CompileOptimized(function, ConcurrencyMode::kConcurrent)) {
     55     return ReadOnlyRoots(isolate).exception();
     56   }
     57   DCHECK(function->is_compiled());
     58   return function->code();
     59 }
     60 
     61 RUNTIME_FUNCTION(Runtime_FunctionFirstExecution) {
     62   HandleScope scope(isolate);
     63   StackLimitCheck check(isolate);
     64   DCHECK_EQ(1, args.length());
     65 
     66   CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
     67   DCHECK_EQ(function->feedback_vector()->optimization_marker(),
     68             OptimizationMarker::kLogFirstExecution);
     69   DCHECK(FLAG_log_function_events);
     70   Handle<SharedFunctionInfo> sfi(function->shared(), isolate);
     71   LOG(isolate, FunctionEvent(
     72                    "first-execution", Script::cast(sfi->script())->id(), 0,
     73                    sfi->StartPosition(), sfi->EndPosition(), sfi->DebugName()));
     74   function->feedback_vector()->ClearOptimizationMarker();
     75   // Return the code to continue execution, we don't care at this point whether
     76   // this is for lazy compilation or has been eagerly complied.
     77   return function->code();
     78 }
     79 
     80 RUNTIME_FUNCTION(Runtime_CompileOptimized_NotConcurrent) {
     81   HandleScope scope(isolate);
     82   DCHECK_EQ(1, args.length());
     83   CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
     84   StackLimitCheck check(isolate);
     85   if (check.JsHasOverflowed(kStackSpaceRequiredForCompilation * KB)) {
     86     return isolate->StackOverflow();
     87   }
     88   if (!Compiler::CompileOptimized(function, ConcurrencyMode::kNotConcurrent)) {
     89     return ReadOnlyRoots(isolate).exception();
     90   }
     91   DCHECK(function->is_compiled());
     92   return function->code();
     93 }
     94 
     95 RUNTIME_FUNCTION(Runtime_EvictOptimizedCodeSlot) {
     96   SealHandleScope scope(isolate);
     97   DCHECK_EQ(1, args.length());
     98   CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
     99 
    100   DCHECK(function->shared()->is_compiled());
    101 
    102   function->feedback_vector()->EvictOptimizedCodeMarkedForDeoptimization(
    103       function->shared(), "Runtime_EvictOptimizedCodeSlot");
    104   return function->code();
    105 }
    106 
    107 RUNTIME_FUNCTION(Runtime_InstantiateAsmJs) {
    108   HandleScope scope(isolate);
    109   DCHECK_EQ(args.length(), 4);
    110   CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
    111 
    112   Handle<JSReceiver> stdlib;
    113   if (args[1]->IsJSReceiver()) {
    114     stdlib = args.at<JSReceiver>(1);
    115   }
    116   Handle<JSReceiver> foreign;
    117   if (args[2]->IsJSReceiver()) {
    118     foreign = args.at<JSReceiver>(2);
    119   }
    120   Handle<JSArrayBuffer> memory;
    121   if (args[3]->IsJSArrayBuffer()) {
    122     memory = args.at<JSArrayBuffer>(3);
    123   }
    124   if (function->shared()->HasAsmWasmData()) {
    125     Handle<SharedFunctionInfo> shared(function->shared(), isolate);
    126     Handle<FixedArray> data(shared->asm_wasm_data(), isolate);
    127     MaybeHandle<Object> result = AsmJs::InstantiateAsmWasm(
    128         isolate, shared, data, stdlib, foreign, memory);
    129     if (!result.is_null()) {
    130       return *result.ToHandleChecked();
    131     }
    132   }
    133   // Remove wasm data, mark as broken for asm->wasm, replace function code with
    134   // UncompiledData, and return a smi 0 to indicate failure.
    135   if (function->shared()->HasAsmWasmData()) {
    136     SharedFunctionInfo::DiscardCompiled(isolate,
    137                                         handle(function->shared(), isolate));
    138   }
    139   function->shared()->set_is_asm_wasm_broken(true);
    140   DCHECK(function->code() ==
    141          isolate->builtins()->builtin(Builtins::kInstantiateAsmJs));
    142   function->set_code(isolate->builtins()->builtin(Builtins::kCompileLazy));
    143   return Smi::kZero;
    144 }
    145 
    146 RUNTIME_FUNCTION(Runtime_NotifyDeoptimized) {
    147   HandleScope scope(isolate);
    148   DCHECK_EQ(0, args.length());
    149   Deoptimizer* deoptimizer = Deoptimizer::Grab(isolate);
    150   DCHECK(deoptimizer->compiled_code()->kind() == Code::OPTIMIZED_FUNCTION);
    151   DCHECK(deoptimizer->compiled_code()->is_turbofanned());
    152   DCHECK(AllowHeapAllocation::IsAllowed());
    153   DCHECK_NULL(isolate->context());
    154 
    155   TimerEventScope<TimerEventDeoptimizeCode> timer(isolate);
    156   TRACE_EVENT0("v8", "V8.DeoptimizeCode");
    157   Handle<JSFunction> function = deoptimizer->function();
    158   DeoptimizeKind type = deoptimizer->deopt_kind();
    159 
    160   // TODO(turbofan): We currently need the native context to materialize
    161   // the arguments object, but only to get to its map.
    162   isolate->set_context(deoptimizer->function()->native_context());
    163 
    164   // Make sure to materialize objects before causing any allocation.
    165   deoptimizer->MaterializeHeapObjects();
    166   delete deoptimizer;
    167 
    168   // Ensure the context register is updated for materialized objects.
    169   JavaScriptFrameIterator top_it(isolate);
    170   JavaScriptFrame* top_frame = top_it.frame();
    171   isolate->set_context(Context::cast(top_frame->context()));
    172 
    173   // Invalidate the underlying optimized code on non-lazy deopts.
    174   if (type != DeoptimizeKind::kLazy) {
    175     Deoptimizer::DeoptimizeFunction(*function);
    176   }
    177 
    178   return ReadOnlyRoots(isolate).undefined_value();
    179 }
    180 
    181 
    182 static bool IsSuitableForOnStackReplacement(Isolate* isolate,
    183                                             Handle<JSFunction> function) {
    184   // Keep track of whether we've succeeded in optimizing.
    185   if (function->shared()->optimization_disabled()) return false;
    186   // If we are trying to do OSR when there are already optimized
    187   // activations of the function, it means (a) the function is directly or
    188   // indirectly recursive and (b) an optimized invocation has been
    189   // deoptimized so that we are currently in an unoptimized activation.
    190   // Check for optimized activations of this function.
    191   for (JavaScriptFrameIterator it(isolate); !it.done(); it.Advance()) {
    192     JavaScriptFrame* frame = it.frame();
    193     if (frame->is_optimized() && frame->function() == *function) return false;
    194   }
    195 
    196   return true;
    197 }
    198 
    199 namespace {
    200 
    201 BailoutId DetermineEntryAndDisarmOSRForInterpreter(JavaScriptFrame* frame) {
    202   InterpretedFrame* iframe = reinterpret_cast<InterpretedFrame*>(frame);
    203 
    204   // Note that the bytecode array active on the stack might be different from
    205   // the one installed on the function (e.g. patched by debugger). This however
    206   // is fine because we guarantee the layout to be in sync, hence any BailoutId
    207   // representing the entry point will be valid for any copy of the bytecode.
    208   Handle<BytecodeArray> bytecode(iframe->GetBytecodeArray(), iframe->isolate());
    209 
    210   DCHECK(frame->LookupCode()->is_interpreter_trampoline_builtin());
    211   DCHECK(frame->function()->shared()->HasBytecodeArray());
    212   DCHECK(frame->is_interpreted());
    213 
    214   // Reset the OSR loop nesting depth to disarm back edges.
    215   bytecode->set_osr_loop_nesting_level(0);
    216 
    217   // Return a BailoutId representing the bytecode offset of the back branch.
    218   return BailoutId(iframe->GetBytecodeOffset());
    219 }
    220 
    221 }  // namespace
    222 
    223 RUNTIME_FUNCTION(Runtime_CompileForOnStackReplacement) {
    224   HandleScope scope(isolate);
    225   DCHECK_EQ(1, args.length());
    226   CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
    227 
    228   // Only reachable when OST is enabled.
    229   CHECK(FLAG_use_osr);
    230 
    231   // Determine frame triggering OSR request.
    232   JavaScriptFrameIterator it(isolate);
    233   JavaScriptFrame* frame = it.frame();
    234   DCHECK_EQ(frame->function(), *function);
    235   DCHECK(frame->is_interpreted());
    236 
    237   // Determine the entry point for which this OSR request has been fired and
    238   // also disarm all back edges in the calling code to stop new requests.
    239   BailoutId ast_id = DetermineEntryAndDisarmOSRForInterpreter(frame);
    240   DCHECK(!ast_id.IsNone());
    241 
    242   MaybeHandle<Code> maybe_result;
    243   if (IsSuitableForOnStackReplacement(isolate, function)) {
    244     if (FLAG_trace_osr) {
    245       PrintF("[OSR - Compiling: ");
    246       function->PrintName();
    247       PrintF(" at AST id %d]\n", ast_id.ToInt());
    248     }
    249     maybe_result = Compiler::GetOptimizedCodeForOSR(function, ast_id, frame);
    250   }
    251 
    252   // Check whether we ended up with usable optimized code.
    253   Handle<Code> result;
    254   if (maybe_result.ToHandle(&result) &&
    255       result->kind() == Code::OPTIMIZED_FUNCTION) {
    256     DeoptimizationData* data =
    257         DeoptimizationData::cast(result->deoptimization_data());
    258 
    259     if (data->OsrPcOffset()->value() >= 0) {
    260       DCHECK(BailoutId(data->OsrBytecodeOffset()->value()) == ast_id);
    261       if (FLAG_trace_osr) {
    262         PrintF("[OSR - Entry at AST id %d, offset %d in optimized code]\n",
    263                ast_id.ToInt(), data->OsrPcOffset()->value());
    264       }
    265 
    266       DCHECK(result->is_turbofanned());
    267       if (!function->HasOptimizedCode()) {
    268         // If we're not already optimized, set to optimize non-concurrently on
    269         // the next call, otherwise we'd run unoptimized once more and
    270         // potentially compile for OSR again.
    271         if (FLAG_trace_osr) {
    272           PrintF("[OSR - Re-marking ");
    273           function->PrintName();
    274           PrintF(" for non-concurrent optimization]\n");
    275         }
    276         function->SetOptimizationMarker(OptimizationMarker::kCompileOptimized);
    277       }
    278       return *result;
    279     }
    280   }
    281 
    282   // Failed.
    283   if (FLAG_trace_osr) {
    284     PrintF("[OSR - Failed: ");
    285     function->PrintName();
    286     PrintF(" at AST id %d]\n", ast_id.ToInt());
    287   }
    288 
    289   if (!function->IsOptimized()) {
    290     function->set_code(function->shared()->GetCode());
    291   }
    292   return nullptr;
    293 }
    294 
    295 static Object* CompileGlobalEval(Isolate* isolate, Handle<String> source,
    296                                  Handle<SharedFunctionInfo> outer_info,
    297                                  LanguageMode language_mode,
    298                                  int eval_scope_position, int eval_position) {
    299   Handle<Context> context(isolate->context(), isolate);
    300   Handle<Context> native_context(context->native_context(), isolate);
    301 
    302   // Check if native context allows code generation from
    303   // strings. Throw an exception if it doesn't.
    304   if (native_context->allow_code_gen_from_strings()->IsFalse(isolate) &&
    305       !Compiler::CodeGenerationFromStringsAllowed(isolate, native_context,
    306                                                   source)) {
    307     Handle<Object> error_message =
    308         native_context->ErrorMessageForCodeGenerationFromStrings();
    309     Handle<Object> error;
    310     MaybeHandle<Object> maybe_error = isolate->factory()->NewEvalError(
    311         MessageTemplate::kCodeGenFromStrings, error_message);
    312     if (maybe_error.ToHandle(&error)) isolate->Throw(*error);
    313     return ReadOnlyRoots(isolate).exception();
    314   }
    315 
    316   // Deal with a normal eval call with a string argument. Compile it
    317   // and return the compiled function bound in the local context.
    318   static const ParseRestriction restriction = NO_PARSE_RESTRICTION;
    319   Handle<JSFunction> compiled;
    320   ASSIGN_RETURN_ON_EXCEPTION_VALUE(
    321       isolate, compiled,
    322       Compiler::GetFunctionFromEval(source, outer_info, context, language_mode,
    323                                     restriction, kNoSourcePosition,
    324                                     eval_scope_position, eval_position),
    325       ReadOnlyRoots(isolate).exception());
    326   return *compiled;
    327 }
    328 
    329 
    330 RUNTIME_FUNCTION(Runtime_ResolvePossiblyDirectEval) {
    331   HandleScope scope(isolate);
    332   DCHECK_EQ(6, args.length());
    333 
    334   Handle<Object> callee = args.at(0);
    335 
    336   // If "eval" didn't refer to the original GlobalEval, it's not a
    337   // direct call to eval.
    338   // (And even if it is, but the first argument isn't a string, just let
    339   // execution default to an indirect call to eval, which will also return
    340   // the first argument without doing anything).
    341   if (*callee != isolate->native_context()->global_eval_fun() ||
    342       !args[1]->IsString()) {
    343     return *callee;
    344   }
    345 
    346   DCHECK(args[3]->IsSmi());
    347   DCHECK(is_valid_language_mode(args.smi_at(3)));
    348   LanguageMode language_mode = static_cast<LanguageMode>(args.smi_at(3));
    349   DCHECK(args[4]->IsSmi());
    350   Handle<SharedFunctionInfo> outer_info(args.at<JSFunction>(2)->shared(),
    351                                         isolate);
    352   return CompileGlobalEval(isolate, args.at<String>(1), outer_info,
    353                            language_mode, args.smi_at(4), args.smi_at(5));
    354 }
    355 }  // namespace internal
    356 }  // namespace v8
    357