418142b5a0
In order for profiles of optimized code to have accurate source positions, we need to prepare for this when compiling. If the profiler is enabled late, this may be missing, leading to inaccurate profile data. A compromise to solve this is to prepare for accurate positions if the debugger (and therefore DevTools) is active, even if we are not currently capturing a profile. The alternative is to deopt everything upon profiling, but that would affect the profile significantly. R=alph@chromium.org, bmeurer@chromium.org, neis@chromium.org, tebbi@chromium.org Review-Url: https://codereview.chromium.org/2519003002 Cr-Commit-Position: refs/heads/master@{#41217}
241 lines
8.2 KiB
C++
241 lines
8.2 KiB
C++
// Copyright 2016 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/compilation-info.h"
|
|
|
|
#include "src/api.h"
|
|
#include "src/ast/ast.h"
|
|
#include "src/ast/scopes.h"
|
|
#include "src/debug/debug.h"
|
|
#include "src/isolate.h"
|
|
#include "src/parsing/parse-info.h"
|
|
#include "src/source-position.h"
|
|
|
|
namespace v8 {
|
|
namespace internal {
|
|
|
|
#define PARSE_INFO_GETTER(type, name) \
|
|
type CompilationInfo::name() const { \
|
|
CHECK(parse_info()); \
|
|
return parse_info()->name(); \
|
|
}
|
|
|
|
#define PARSE_INFO_GETTER_WITH_DEFAULT(type, name, def) \
|
|
type CompilationInfo::name() const { \
|
|
return parse_info() ? parse_info()->name() : def; \
|
|
}
|
|
|
|
PARSE_INFO_GETTER(Handle<Script>, script)
|
|
PARSE_INFO_GETTER(FunctionLiteral*, literal)
|
|
PARSE_INFO_GETTER_WITH_DEFAULT(DeclarationScope*, scope, nullptr)
|
|
PARSE_INFO_GETTER(Handle<SharedFunctionInfo>, shared_info)
|
|
|
|
#undef PARSE_INFO_GETTER
|
|
#undef PARSE_INFO_GETTER_WITH_DEFAULT
|
|
|
|
bool CompilationInfo::is_debug() const {
|
|
return parse_info() ? parse_info()->is_debug() : false;
|
|
}
|
|
|
|
void CompilationInfo::set_is_debug() {
|
|
CHECK(parse_info());
|
|
parse_info()->set_is_debug();
|
|
}
|
|
|
|
void CompilationInfo::PrepareForSerializing() {
|
|
if (parse_info()) parse_info()->set_will_serialize();
|
|
SetFlag(kSerializing);
|
|
}
|
|
|
|
bool CompilationInfo::has_shared_info() const {
|
|
return parse_info_ && !parse_info_->shared_info().is_null();
|
|
}
|
|
|
|
CompilationInfo::CompilationInfo(ParseInfo* parse_info,
|
|
Handle<JSFunction> closure)
|
|
: CompilationInfo(parse_info, {}, Code::ComputeFlags(Code::FUNCTION), BASE,
|
|
parse_info->isolate(), parse_info->zone()) {
|
|
closure_ = closure;
|
|
|
|
// Compiling for the snapshot typically results in different code than
|
|
// compiling later on. This means that code recompiled with deoptimization
|
|
// support won't be "equivalent" (as defined by SharedFunctionInfo::
|
|
// EnableDeoptimizationSupport), so it will replace the old code and all
|
|
// its type feedback. To avoid this, always compile functions in the snapshot
|
|
// with deoptimization support.
|
|
if (isolate_->serializer_enabled()) EnableDeoptimizationSupport();
|
|
|
|
if (FLAG_function_context_specialization) MarkAsFunctionContextSpecializing();
|
|
if (FLAG_turbo_splitting) MarkAsSplittingEnabled();
|
|
|
|
// Collect source positions for optimized code when profiling or if debugger
|
|
// is active, to be able to get more precise source positions at the price of
|
|
// more memory consumption.
|
|
if (FLAG_trace_deopt || FLAG_trace_turbo || FLAG_trace_turbo_graph ||
|
|
FLAG_turbo_profiling || isolate_->is_profiling() ||
|
|
isolate_->debug()->is_active()) {
|
|
MarkAsSourcePositionsEnabled();
|
|
}
|
|
}
|
|
|
|
CompilationInfo::CompilationInfo(Vector<const char> debug_name,
|
|
Isolate* isolate, Zone* zone,
|
|
Code::Flags code_flags)
|
|
: CompilationInfo(nullptr, debug_name, code_flags, STUB, isolate, zone) {}
|
|
|
|
CompilationInfo::CompilationInfo(ParseInfo* parse_info,
|
|
Vector<const char> debug_name,
|
|
Code::Flags code_flags, Mode mode,
|
|
Isolate* isolate, Zone* zone)
|
|
: parse_info_(parse_info),
|
|
isolate_(isolate),
|
|
flags_(0),
|
|
code_flags_(code_flags),
|
|
mode_(mode),
|
|
osr_ast_id_(BailoutId::None()),
|
|
zone_(zone),
|
|
deferred_handles_(nullptr),
|
|
dependencies_(isolate, zone),
|
|
bailout_reason_(kNoReason),
|
|
prologue_offset_(Code::kPrologueOffsetNotSet),
|
|
parameter_count_(0),
|
|
optimization_id_(-1),
|
|
osr_expr_stack_height_(-1),
|
|
debug_name_(debug_name) {}
|
|
|
|
CompilationInfo::~CompilationInfo() {
|
|
if (GetFlag(kDisableFutureOptimization) && has_shared_info()) {
|
|
shared_info()->DisableOptimization(bailout_reason());
|
|
}
|
|
dependencies()->Rollback();
|
|
delete deferred_handles_;
|
|
}
|
|
|
|
int CompilationInfo::num_parameters() const {
|
|
return !IsStub() ? scope()->num_parameters() : parameter_count_;
|
|
}
|
|
|
|
int CompilationInfo::num_parameters_including_this() const {
|
|
return num_parameters() + (is_this_defined() ? 1 : 0);
|
|
}
|
|
|
|
bool CompilationInfo::is_this_defined() const { return !IsStub(); }
|
|
|
|
// Primitive functions are unlikely to be picked up by the stack-walking
|
|
// profiler, so they trigger their own optimization when they're called
|
|
// for the SharedFunctionInfo::kCallsUntilPrimitiveOptimization-th time.
|
|
bool CompilationInfo::ShouldSelfOptimize() {
|
|
return FLAG_crankshaft &&
|
|
!(literal()->flags() & AstProperties::kDontSelfOptimize) &&
|
|
!literal()->dont_optimize() &&
|
|
literal()->scope()->AllowsLazyCompilation() &&
|
|
!shared_info()->optimization_disabled();
|
|
}
|
|
|
|
void CompilationInfo::ReopenHandlesInNewHandleScope() {
|
|
closure_ = Handle<JSFunction>(*closure_);
|
|
}
|
|
|
|
bool CompilationInfo::has_simple_parameters() {
|
|
return scope()->has_simple_parameters();
|
|
}
|
|
|
|
std::unique_ptr<char[]> CompilationInfo::GetDebugName() const {
|
|
if (parse_info() && parse_info()->literal()) {
|
|
AllowHandleDereference allow_deref;
|
|
return parse_info()->literal()->debug_name()->ToCString();
|
|
}
|
|
if (parse_info() && !parse_info()->shared_info().is_null()) {
|
|
return parse_info()->shared_info()->DebugName()->ToCString();
|
|
}
|
|
Vector<const char> name_vec = debug_name_;
|
|
if (name_vec.is_empty()) name_vec = ArrayVector("unknown");
|
|
std::unique_ptr<char[]> name(new char[name_vec.length() + 1]);
|
|
memcpy(name.get(), name_vec.start(), name_vec.length());
|
|
name[name_vec.length()] = '\0';
|
|
return name;
|
|
}
|
|
|
|
StackFrame::Type CompilationInfo::GetOutputStackFrameType() const {
|
|
switch (output_code_kind()) {
|
|
case Code::STUB:
|
|
case Code::BYTECODE_HANDLER:
|
|
case Code::HANDLER:
|
|
case Code::BUILTIN:
|
|
#define CASE_KIND(kind) case Code::kind:
|
|
IC_KIND_LIST(CASE_KIND)
|
|
#undef CASE_KIND
|
|
return StackFrame::STUB;
|
|
case Code::WASM_FUNCTION:
|
|
return StackFrame::WASM;
|
|
case Code::JS_TO_WASM_FUNCTION:
|
|
return StackFrame::JS_TO_WASM;
|
|
case Code::WASM_TO_JS_FUNCTION:
|
|
return StackFrame::WASM_TO_JS;
|
|
default:
|
|
UNIMPLEMENTED();
|
|
return StackFrame::NONE;
|
|
}
|
|
}
|
|
|
|
int CompilationInfo::GetDeclareGlobalsFlags() const {
|
|
DCHECK(DeclareGlobalsLanguageMode::is_valid(parse_info()->language_mode()));
|
|
return DeclareGlobalsEvalFlag::encode(parse_info()->is_eval()) |
|
|
DeclareGlobalsNativeFlag::encode(parse_info()->is_native()) |
|
|
DeclareGlobalsLanguageMode::encode(parse_info()->language_mode());
|
|
}
|
|
|
|
SourcePositionTableBuilder::RecordingMode
|
|
CompilationInfo::SourcePositionRecordingMode() const {
|
|
return parse_info() && parse_info()->is_native()
|
|
? SourcePositionTableBuilder::OMIT_SOURCE_POSITIONS
|
|
: SourcePositionTableBuilder::RECORD_SOURCE_POSITIONS;
|
|
}
|
|
|
|
bool CompilationInfo::ExpectsJSReceiverAsReceiver() {
|
|
return is_sloppy(parse_info()->language_mode()) && !parse_info()->is_native();
|
|
}
|
|
|
|
bool CompilationInfo::has_context() const { return !closure().is_null(); }
|
|
|
|
Context* CompilationInfo::context() const {
|
|
return has_context() ? closure()->context() : nullptr;
|
|
}
|
|
|
|
bool CompilationInfo::has_native_context() const {
|
|
return !closure().is_null() && (closure()->native_context() != nullptr);
|
|
}
|
|
|
|
Context* CompilationInfo::native_context() const {
|
|
return has_native_context() ? closure()->native_context() : nullptr;
|
|
}
|
|
|
|
bool CompilationInfo::has_global_object() const { return has_native_context(); }
|
|
|
|
JSGlobalObject* CompilationInfo::global_object() const {
|
|
return has_global_object() ? native_context()->global_object() : nullptr;
|
|
}
|
|
|
|
void CompilationInfo::SetOptimizing() {
|
|
DCHECK(has_shared_info());
|
|
SetMode(OPTIMIZE);
|
|
optimization_id_ = isolate()->NextOptimizationId();
|
|
code_flags_ = Code::KindField::update(code_flags_, Code::OPTIMIZED_FUNCTION);
|
|
}
|
|
|
|
int CompilationInfo::AddInlinedFunction(
|
|
Handle<SharedFunctionInfo> inlined_function, SourcePosition pos) {
|
|
int id = static_cast<int>(inlined_functions_.size());
|
|
inlined_functions_.push_back(InlinedFunctionHolder(
|
|
inlined_function, handle(inlined_function->code()), pos));
|
|
return id;
|
|
}
|
|
|
|
Code::Kind CompilationInfo::output_code_kind() const {
|
|
return Code::ExtractKindFromFlags(code_flags_);
|
|
}
|
|
|
|
} // namespace internal
|
|
} // namespace v8
|