| Index: src/runtime/runtime-compiler.cc
|
| diff --git a/src/runtime/runtime-compiler.cc b/src/runtime/runtime-compiler.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..3f7e936d3b0da94dc3fb7f1f66efec089f7c8470
|
| --- /dev/null
|
| +++ b/src/runtime/runtime-compiler.cc
|
| @@ -0,0 +1,441 @@
|
| +// Copyright 2014 the V8 project authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "src/v8.h"
|
| +
|
| +#include "src/arguments.h"
|
| +#include "src/compiler.h"
|
| +#include "src/deoptimizer.h"
|
| +#include "src/frames.h"
|
| +#include "src/full-codegen.h"
|
| +#include "src/isolate.h"
|
| +#include "src/isolate-inl.h"
|
| +#include "src/runtime/runtime.h"
|
| +#include "src/runtime/runtime-utils.h"
|
| +#include "src/v8threads.h"
|
| +#include "src/vm-state.h"
|
| +#include "src/vm-state-inl.h"
|
| +
|
| +namespace v8 {
|
| +namespace internal {
|
| +
|
| +RUNTIME_FUNCTION(Runtime_CompileLazy) {
|
| + HandleScope scope(isolate);
|
| + DCHECK(args.length() == 1);
|
| + CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
|
| +#ifdef DEBUG
|
| + if (FLAG_trace_lazy && !function->shared()->is_compiled()) {
|
| + PrintF("[unoptimized: ");
|
| + function->PrintName();
|
| + PrintF("]\n");
|
| + }
|
| +#endif
|
| +
|
| + // Compile the target function.
|
| + DCHECK(function->shared()->allows_lazy_compilation());
|
| +
|
| + Handle<Code> code;
|
| + ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, code,
|
| + Compiler::GetLazyCode(function));
|
| + DCHECK(code->kind() == Code::FUNCTION ||
|
| + code->kind() == Code::OPTIMIZED_FUNCTION);
|
| + function->ReplaceCode(*code);
|
| + return *code;
|
| +}
|
| +
|
| +
|
| +RUNTIME_FUNCTION(Runtime_CompileOptimized) {
|
| + HandleScope scope(isolate);
|
| + DCHECK(args.length() == 2);
|
| + CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
|
| + CONVERT_BOOLEAN_ARG_CHECKED(concurrent, 1);
|
| +
|
| + Handle<Code> unoptimized(function->shared()->code());
|
| + if (!isolate->use_crankshaft() ||
|
| + function->shared()->optimization_disabled() ||
|
| + isolate->DebuggerHasBreakPoints()) {
|
| + // If the function is not optimizable or debugger is active continue
|
| + // using the code from the full compiler.
|
| + if (FLAG_trace_opt) {
|
| + PrintF("[failed to optimize ");
|
| + function->PrintName();
|
| + PrintF(": is code optimizable: %s, is debugger enabled: %s]\n",
|
| + function->shared()->optimization_disabled() ? "F" : "T",
|
| + isolate->DebuggerHasBreakPoints() ? "T" : "F");
|
| + }
|
| + function->ReplaceCode(*unoptimized);
|
| + return function->code();
|
| + }
|
| +
|
| + Compiler::ConcurrencyMode mode =
|
| + concurrent ? Compiler::CONCURRENT : Compiler::NOT_CONCURRENT;
|
| + Handle<Code> code;
|
| + if (Compiler::GetOptimizedCode(function, unoptimized, mode).ToHandle(&code)) {
|
| + function->ReplaceCode(*code);
|
| + } else {
|
| + function->ReplaceCode(function->shared()->code());
|
| + }
|
| +
|
| + DCHECK(function->code()->kind() == Code::FUNCTION ||
|
| + function->code()->kind() == Code::OPTIMIZED_FUNCTION ||
|
| + function->IsInOptimizationQueue());
|
| + return function->code();
|
| +}
|
| +
|
| +
|
| +RUNTIME_FUNCTION(Runtime_NotifyStubFailure) {
|
| + HandleScope scope(isolate);
|
| + DCHECK(args.length() == 0);
|
| + Deoptimizer* deoptimizer = Deoptimizer::Grab(isolate);
|
| + DCHECK(AllowHeapAllocation::IsAllowed());
|
| + delete deoptimizer;
|
| + return isolate->heap()->undefined_value();
|
| +}
|
| +
|
| +
|
| +class ActivationsFinder : public ThreadVisitor {
|
| + public:
|
| + Code* code_;
|
| + bool has_code_activations_;
|
| +
|
| + explicit ActivationsFinder(Code* code)
|
| + : code_(code), has_code_activations_(false) {}
|
| +
|
| + void VisitThread(Isolate* isolate, ThreadLocalTop* top) {
|
| + JavaScriptFrameIterator it(isolate, top);
|
| + VisitFrames(&it);
|
| + }
|
| +
|
| + void VisitFrames(JavaScriptFrameIterator* it) {
|
| + for (; !it->done(); it->Advance()) {
|
| + JavaScriptFrame* frame = it->frame();
|
| + if (code_->contains(frame->pc())) has_code_activations_ = true;
|
| + }
|
| + }
|
| +};
|
| +
|
| +
|
| +RUNTIME_FUNCTION(Runtime_NotifyDeoptimized) {
|
| + HandleScope scope(isolate);
|
| + DCHECK(args.length() == 1);
|
| + CONVERT_SMI_ARG_CHECKED(type_arg, 0);
|
| + Deoptimizer::BailoutType type =
|
| + static_cast<Deoptimizer::BailoutType>(type_arg);
|
| + Deoptimizer* deoptimizer = Deoptimizer::Grab(isolate);
|
| + DCHECK(AllowHeapAllocation::IsAllowed());
|
| +
|
| + Handle<JSFunction> function = deoptimizer->function();
|
| + Handle<Code> optimized_code = deoptimizer->compiled_code();
|
| +
|
| + DCHECK(optimized_code->kind() == Code::OPTIMIZED_FUNCTION);
|
| + DCHECK(type == deoptimizer->bailout_type());
|
| +
|
| + // Make sure to materialize objects before causing any allocation.
|
| + JavaScriptFrameIterator it(isolate);
|
| + deoptimizer->MaterializeHeapObjects(&it);
|
| + delete deoptimizer;
|
| +
|
| + JavaScriptFrame* frame = it.frame();
|
| + RUNTIME_ASSERT(frame->function()->IsJSFunction());
|
| + DCHECK(frame->function() == *function);
|
| +
|
| + // Avoid doing too much work when running with --always-opt and keep
|
| + // the optimized code around.
|
| + if (FLAG_always_opt || type == Deoptimizer::LAZY) {
|
| + return isolate->heap()->undefined_value();
|
| + }
|
| +
|
| + // Search for other activations of the same function and code.
|
| + ActivationsFinder activations_finder(*optimized_code);
|
| + activations_finder.VisitFrames(&it);
|
| + isolate->thread_manager()->IterateArchivedThreads(&activations_finder);
|
| +
|
| + if (!activations_finder.has_code_activations_) {
|
| + if (function->code() == *optimized_code) {
|
| + if (FLAG_trace_deopt) {
|
| + PrintF("[removing optimized code for: ");
|
| + function->PrintName();
|
| + PrintF("]\n");
|
| + }
|
| + function->ReplaceCode(function->shared()->code());
|
| + // Evict optimized code for this function from the cache so that it
|
| + // doesn't get used for new closures.
|
| + function->shared()->EvictFromOptimizedCodeMap(*optimized_code,
|
| + "notify deoptimized");
|
| + }
|
| + } else {
|
| + // TODO(titzer): we should probably do DeoptimizeCodeList(code)
|
| + // unconditionally if the code is not already marked for deoptimization.
|
| + // If there is an index by shared function info, all the better.
|
| + Deoptimizer::DeoptimizeFunction(*function);
|
| + }
|
| +
|
| + return isolate->heap()->undefined_value();
|
| +}
|
| +
|
| +
|
| +static bool IsSuitableForOnStackReplacement(Isolate* isolate,
|
| + Handle<JSFunction> function,
|
| + Handle<Code> current_code) {
|
| + // Keep track of whether we've succeeded in optimizing.
|
| + if (!isolate->use_crankshaft() || !current_code->optimizable()) return false;
|
| + // If we are trying to do OSR when there are already optimized
|
| + // activations of the function, it means (a) the function is directly or
|
| + // indirectly recursive and (b) an optimized invocation has been
|
| + // deoptimized so that we are currently in an unoptimized activation.
|
| + // Check for optimized activations of this function.
|
| + for (JavaScriptFrameIterator it(isolate); !it.done(); it.Advance()) {
|
| + JavaScriptFrame* frame = it.frame();
|
| + if (frame->is_optimized() && frame->function() == *function) return false;
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +
|
| +RUNTIME_FUNCTION(Runtime_CompileForOnStackReplacement) {
|
| + HandleScope scope(isolate);
|
| + DCHECK(args.length() == 1);
|
| + CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
|
| + Handle<Code> caller_code(function->shared()->code());
|
| +
|
| + // We're not prepared to handle a function with arguments object.
|
| + DCHECK(!function->shared()->uses_arguments());
|
| +
|
| + RUNTIME_ASSERT(FLAG_use_osr);
|
| +
|
| + // Passing the PC in the javascript frame from the caller directly is
|
| + // not GC safe, so we walk the stack to get it.
|
| + JavaScriptFrameIterator it(isolate);
|
| + JavaScriptFrame* frame = it.frame();
|
| + if (!caller_code->contains(frame->pc())) {
|
| + // Code on the stack may not be the code object referenced by the shared
|
| + // function info. It may have been replaced to include deoptimization data.
|
| + caller_code = Handle<Code>(frame->LookupCode());
|
| + }
|
| +
|
| + uint32_t pc_offset =
|
| + static_cast<uint32_t>(frame->pc() - caller_code->instruction_start());
|
| +
|
| +#ifdef DEBUG
|
| + DCHECK_EQ(frame->function(), *function);
|
| + DCHECK_EQ(frame->LookupCode(), *caller_code);
|
| + DCHECK(caller_code->contains(frame->pc()));
|
| +#endif // DEBUG
|
| +
|
| +
|
| + BailoutId ast_id = caller_code->TranslatePcOffsetToAstId(pc_offset);
|
| + DCHECK(!ast_id.IsNone());
|
| +
|
| + Compiler::ConcurrencyMode mode =
|
| + isolate->concurrent_osr_enabled() &&
|
| + (function->shared()->ast_node_count() > 512)
|
| + ? Compiler::CONCURRENT
|
| + : Compiler::NOT_CONCURRENT;
|
| + Handle<Code> result = Handle<Code>::null();
|
| +
|
| + OptimizedCompileJob* job = NULL;
|
| + if (mode == Compiler::CONCURRENT) {
|
| + // Gate the OSR entry with a stack check.
|
| + BackEdgeTable::AddStackCheck(caller_code, pc_offset);
|
| + // Poll already queued compilation jobs.
|
| + OptimizingCompilerThread* thread = isolate->optimizing_compiler_thread();
|
| + if (thread->IsQueuedForOSR(function, ast_id)) {
|
| + if (FLAG_trace_osr) {
|
| + PrintF("[OSR - Still waiting for queued: ");
|
| + function->PrintName();
|
| + PrintF(" at AST id %d]\n", ast_id.ToInt());
|
| + }
|
| + return NULL;
|
| + }
|
| +
|
| + job = thread->FindReadyOSRCandidate(function, ast_id);
|
| + }
|
| +
|
| + if (job != NULL) {
|
| + if (FLAG_trace_osr) {
|
| + PrintF("[OSR - Found ready: ");
|
| + function->PrintName();
|
| + PrintF(" at AST id %d]\n", ast_id.ToInt());
|
| + }
|
| + result = Compiler::GetConcurrentlyOptimizedCode(job);
|
| + } else if (IsSuitableForOnStackReplacement(isolate, function, caller_code)) {
|
| + if (FLAG_trace_osr) {
|
| + PrintF("[OSR - Compiling: ");
|
| + function->PrintName();
|
| + PrintF(" at AST id %d]\n", ast_id.ToInt());
|
| + }
|
| + MaybeHandle<Code> maybe_result =
|
| + Compiler::GetOptimizedCode(function, caller_code, mode, ast_id);
|
| + if (maybe_result.ToHandle(&result) &&
|
| + result.is_identical_to(isolate->builtins()->InOptimizationQueue())) {
|
| + // Optimization is queued. Return to check later.
|
| + return NULL;
|
| + }
|
| + }
|
| +
|
| + // Revert the patched back edge table, regardless of whether OSR succeeds.
|
| + BackEdgeTable::Revert(isolate, *caller_code);
|
| +
|
| + // Check whether we ended up with usable optimized code.
|
| + if (!result.is_null() && result->kind() == Code::OPTIMIZED_FUNCTION) {
|
| + DeoptimizationInputData* data =
|
| + DeoptimizationInputData::cast(result->deoptimization_data());
|
| +
|
| + if (data->OsrPcOffset()->value() >= 0) {
|
| + DCHECK(BailoutId(data->OsrAstId()->value()) == ast_id);
|
| + if (FLAG_trace_osr) {
|
| + PrintF("[OSR - Entry at AST id %d, offset %d in optimized code]\n",
|
| + ast_id.ToInt(), data->OsrPcOffset()->value());
|
| + }
|
| + // TODO(titzer): this is a massive hack to make the deopt counts
|
| + // match. Fix heuristics for reenabling optimizations!
|
| + function->shared()->increment_deopt_count();
|
| +
|
| + // TODO(titzer): Do not install code into the function.
|
| + function->ReplaceCode(*result);
|
| + return *result;
|
| + }
|
| + }
|
| +
|
| + // Failed.
|
| + if (FLAG_trace_osr) {
|
| + PrintF("[OSR - Failed: ");
|
| + function->PrintName();
|
| + PrintF(" at AST id %d]\n", ast_id.ToInt());
|
| + }
|
| +
|
| + if (!function->IsOptimized()) {
|
| + function->ReplaceCode(function->shared()->code());
|
| + }
|
| + return NULL;
|
| +}
|
| +
|
| +
|
| +RUNTIME_FUNCTION(Runtime_TryInstallOptimizedCode) {
|
| + HandleScope scope(isolate);
|
| + DCHECK(args.length() == 1);
|
| + CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
|
| +
|
| + // First check if this is a real stack overflow.
|
| + StackLimitCheck check(isolate);
|
| + if (check.JsHasOverflowed()) {
|
| + SealHandleScope shs(isolate);
|
| + return isolate->StackOverflow();
|
| + }
|
| +
|
| + isolate->optimizing_compiler_thread()->InstallOptimizedFunctions();
|
| + return (function->IsOptimized()) ? function->code()
|
| + : function->shared()->code();
|
| +}
|
| +
|
| +
|
| +bool CodeGenerationFromStringsAllowed(Isolate* isolate,
|
| + Handle<Context> context) {
|
| + DCHECK(context->allow_code_gen_from_strings()->IsFalse());
|
| + // Check with callback if set.
|
| + AllowCodeGenerationFromStringsCallback callback =
|
| + isolate->allow_code_gen_callback();
|
| + if (callback == NULL) {
|
| + // No callback set and code generation disallowed.
|
| + return false;
|
| + } else {
|
| + // Callback set. Let it decide if code generation is allowed.
|
| + VMState<EXTERNAL> state(isolate);
|
| + return callback(v8::Utils::ToLocal(context));
|
| + }
|
| +}
|
| +
|
| +
|
| +RUNTIME_FUNCTION(Runtime_CompileString) {
|
| + HandleScope scope(isolate);
|
| + DCHECK(args.length() == 2);
|
| + CONVERT_ARG_HANDLE_CHECKED(String, source, 0);
|
| + CONVERT_BOOLEAN_ARG_CHECKED(function_literal_only, 1);
|
| +
|
| + // Extract native context.
|
| + Handle<Context> context(isolate->native_context());
|
| +
|
| + // Check if native context allows code generation from
|
| + // strings. Throw an exception if it doesn't.
|
| + if (context->allow_code_gen_from_strings()->IsFalse() &&
|
| + !CodeGenerationFromStringsAllowed(isolate, context)) {
|
| + Handle<Object> error_message =
|
| + context->ErrorMessageForCodeGenerationFromStrings();
|
| + THROW_NEW_ERROR_RETURN_FAILURE(
|
| + isolate, NewEvalError("code_gen_from_strings",
|
| + HandleVector<Object>(&error_message, 1)));
|
| + }
|
| +
|
| + // Compile source string in the native context.
|
| + ParseRestriction restriction = function_literal_only
|
| + ? ONLY_SINGLE_FUNCTION_LITERAL
|
| + : NO_PARSE_RESTRICTION;
|
| + Handle<JSFunction> fun;
|
| + ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
|
| + isolate, fun,
|
| + Compiler::GetFunctionFromEval(source, context, SLOPPY, restriction,
|
| + RelocInfo::kNoPosition));
|
| + return *fun;
|
| +}
|
| +
|
| +
|
| +static ObjectPair CompileGlobalEval(Isolate* isolate, Handle<String> source,
|
| + Handle<Object> receiver,
|
| + StrictMode strict_mode,
|
| + int scope_position) {
|
| + Handle<Context> context = Handle<Context>(isolate->context());
|
| + Handle<Context> native_context = Handle<Context>(context->native_context());
|
| +
|
| + // Check if native context allows code generation from
|
| + // strings. Throw an exception if it doesn't.
|
| + if (native_context->allow_code_gen_from_strings()->IsFalse() &&
|
| + !CodeGenerationFromStringsAllowed(isolate, native_context)) {
|
| + Handle<Object> error_message =
|
| + native_context->ErrorMessageForCodeGenerationFromStrings();
|
| + Handle<Object> error;
|
| + MaybeHandle<Object> maybe_error = isolate->factory()->NewEvalError(
|
| + "code_gen_from_strings", HandleVector<Object>(&error_message, 1));
|
| + if (maybe_error.ToHandle(&error)) isolate->Throw(*error);
|
| + return MakePair(isolate->heap()->exception(), NULL);
|
| + }
|
| +
|
| + // Deal with a normal eval call with a string argument. Compile it
|
| + // and return the compiled function bound in the local context.
|
| + static const ParseRestriction restriction = NO_PARSE_RESTRICTION;
|
| + Handle<JSFunction> compiled;
|
| + ASSIGN_RETURN_ON_EXCEPTION_VALUE(
|
| + isolate, compiled,
|
| + Compiler::GetFunctionFromEval(source, context, strict_mode, restriction,
|
| + scope_position),
|
| + MakePair(isolate->heap()->exception(), NULL));
|
| + return MakePair(*compiled, *receiver);
|
| +}
|
| +
|
| +
|
| +RUNTIME_FUNCTION_RETURN_PAIR(Runtime_ResolvePossiblyDirectEval) {
|
| + HandleScope scope(isolate);
|
| + DCHECK(args.length() == 5);
|
| +
|
| + Handle<Object> callee = args.at<Object>(0);
|
| +
|
| + // If "eval" didn't refer to the original GlobalEval, it's not a
|
| + // direct call to eval.
|
| + // (And even if it is, but the first argument isn't a string, just let
|
| + // execution default to an indirect call to eval, which will also return
|
| + // the first argument without doing anything).
|
| + if (*callee != isolate->native_context()->global_eval_fun() ||
|
| + !args[1]->IsString()) {
|
| + return MakePair(*callee, isolate->heap()->undefined_value());
|
| + }
|
| +
|
| + DCHECK(args[3]->IsSmi());
|
| + DCHECK(args.smi_at(3) == SLOPPY || args.smi_at(3) == STRICT);
|
| + StrictMode strict_mode = static_cast<StrictMode>(args.smi_at(3));
|
| + DCHECK(args[4]->IsSmi());
|
| + return CompileGlobalEval(isolate, args.at<String>(1), args.at<Object>(2),
|
| + strict_mode, args.smi_at(4));
|
| +}
|
| +}
|
| +} // namespace v8::internal
|
|
|