diff --git a/BUILD.gn b/BUILD.gn index 2628b9dbcf..be1c01bed5 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -474,6 +474,8 @@ source_set("v8_base") { "src/codegen.h", "src/compilation-cache.cc", "src/compilation-cache.h", + "src/compilation-statistics.cc", + "src/compilation-statistics.h", "src/compiler/access-builder.cc", "src/compiler/access-builder.h", "src/compiler/ast-graph-builder.cc", @@ -560,6 +562,8 @@ source_set("v8_base") { "src/compiler/phi-reducer.h", "src/compiler/pipeline.cc", "src/compiler/pipeline.h", + "src/compiler/pipeline-statistics.cc", + "src/compiler/pipeline-statistics.h", "src/compiler/raw-machine-assembler.cc", "src/compiler/raw-machine-assembler.h", "src/compiler/register-allocator.cc", diff --git a/src/compilation-statistics.cc b/src/compilation-statistics.cc new file mode 100644 index 0000000000..056fd62466 --- /dev/null +++ b/src/compilation-statistics.cc @@ -0,0 +1,140 @@ +// 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 // NOLINT(readability/streams) +#include + +#include "src/base/platform/platform.h" +#include "src/compilation-statistics.h" + +namespace v8 { +namespace internal { + +void CompilationStatistics::RecordPhaseStats(const char* phase_kind_name, + const char* phase_name, + const BasicStats& stats) { + std::string phase_name_str(phase_name); + auto it = phase_map_.find(phase_name_str); + if (it == phase_map_.end()) { + PhaseStats phase_stats(phase_map_.size(), phase_kind_name); + it = phase_map_.insert(std::make_pair(phase_name_str, phase_stats)).first; + } + it->second.Accumulate(stats); +} + + +void CompilationStatistics::RecordPhaseKindStats(const char* phase_kind_name, + const BasicStats& stats) { + std::string phase_kind_name_str(phase_kind_name); + auto it = phase_kind_map_.find(phase_kind_name_str); + if (it == phase_kind_map_.end()) { + PhaseKindStats phase_kind_stats(phase_kind_map_.size()); + it = phase_kind_map_.insert(std::make_pair(phase_kind_name_str, + phase_kind_stats)).first; + } + it->second.Accumulate(stats); +} + + +void CompilationStatistics::RecordTotalStats(size_t source_size, + const BasicStats& stats) { + source_size += source_size; + total_stats_.Accumulate(stats); +} + + +void CompilationStatistics::BasicStats::Accumulate(const BasicStats& stats) { + delta_ += stats.delta_; + total_allocated_bytes_ += stats.total_allocated_bytes_; + if (stats.max_allocated_bytes_ > max_allocated_bytes_) { + max_allocated_bytes_ = stats.max_allocated_bytes_; + function_name_ = stats.function_name_; + } +} + + +static void WriteLine(std::ostream& os, const char* name, + const CompilationStatistics::BasicStats& stats, + const CompilationStatistics::BasicStats& total_stats) { + const size_t kBufferSize = 128; + char buffer[kBufferSize]; + + double ms = stats.delta_.InMillisecondsF(); + double percent = stats.delta_.PercentOf(total_stats.delta_); + double size_percent = + static_cast(stats.total_allocated_bytes_ * 100) / + static_cast(total_stats.total_allocated_bytes_); + base::OS::SNPrintF(buffer, kBufferSize, + "%28s %10.3f ms / %5.1f %% %10u total / %5.1f %% %10u max", + name, ms, percent, stats.total_allocated_bytes_, + size_percent, stats.max_allocated_bytes_); + + os << buffer; + if (stats.function_name_.size() > 0) { + os << " : " << stats.function_name_.c_str(); + } + os << std::endl; +} + + +static void WriteFullLine(std::ostream& os) { + os << "-----------------------------------------------" + "-----------------------------------------------\n"; +} + + +static void WriteHeader(std::ostream& os) { + WriteFullLine(os); + os << " Turbofan timing results:\n"; + WriteFullLine(os); +} + + +static void WritePhaseKindBreak(std::ostream& os) { + os << " ------------------" + "-----------------------------------------------\n"; +} + + +std::ostream& operator<<(std::ostream& os, const CompilationStatistics& s) { + // phase_kind_map_ and phase_map_ don't get mutated, so store a bunch of + // pointers into them. + + typedef std::vector + SortedPhaseKinds; + SortedPhaseKinds sorted_phase_kinds(s.phase_kind_map_.size()); + for (auto it = s.phase_kind_map_.begin(); it != s.phase_kind_map_.end(); + ++it) { + sorted_phase_kinds[it->second.insert_order_] = it; + } + + typedef std::vector + SortedPhases; + SortedPhases sorted_phases(s.phase_map_.size()); + for (auto it = s.phase_map_.begin(); it != s.phase_map_.end(); ++it) { + sorted_phases[it->second.insert_order_] = it; + } + + WriteHeader(os); + for (auto phase_kind_it : sorted_phase_kinds) { + const auto& phase_kind_name = phase_kind_it->first; + for (auto phase_it : sorted_phases) { + const auto& phase_stats = phase_it->second; + if (phase_stats.phase_kind_name_ != phase_kind_name) continue; + const auto& phase_name = phase_it->first; + WriteLine(os, phase_name.c_str(), phase_stats, s.total_stats_); + } + WritePhaseKindBreak(os); + const auto& phase_kind_stats = phase_kind_it->second; + WriteLine(os, phase_kind_name.c_str(), phase_kind_stats, s.total_stats_); + os << std::endl; + } + WriteFullLine(os); + WriteLine(os, "totals", s.total_stats_, s.total_stats_); + + return os; +} + +} // namespace internal +} // namespace v8 diff --git a/src/compilation-statistics.h b/src/compilation-statistics.h new file mode 100644 index 0000000000..62fefe3d73 --- /dev/null +++ b/src/compilation-statistics.h @@ -0,0 +1,82 @@ +// 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. + +#ifndef V8_COMPILATION_STATISTICS_H_ +#define V8_COMPILATION_STATISTICS_H_ + +#include +#include + +#include "src/allocation.h" +#include "src/base/platform/time.h" + +namespace v8 { +namespace internal { + +class CompilationInfo; + +class CompilationStatistics FINAL : public Malloced { + public: + CompilationStatistics() {} + + class BasicStats { + public: + BasicStats() : total_allocated_bytes_(0), max_allocated_bytes_(0) {} + + void Accumulate(const BasicStats& stats); + + base::TimeDelta delta_; + size_t total_allocated_bytes_; + size_t max_allocated_bytes_; + std::string function_name_; + }; + + void RecordPhaseStats(const char* phase_kind_name, const char* phase_name, + const BasicStats& stats); + + void RecordPhaseKindStats(const char* phase_kind_name, + const BasicStats& stats); + + void RecordTotalStats(size_t source_size, const BasicStats& stats); + + private: + class TotalStats : public BasicStats { + public: + TotalStats() : source_size_(0) {} + uint64_t source_size_; + }; + + class OrderedStats : public BasicStats { + public: + explicit OrderedStats(size_t insert_order) : insert_order_(insert_order) {} + size_t insert_order_; + }; + + class PhaseStats : public OrderedStats { + public: + PhaseStats(size_t insert_order, const char* phase_kind_name) + : OrderedStats(insert_order), phase_kind_name_(phase_kind_name) {} + std::string phase_kind_name_; + }; + + friend std::ostream& operator<<(std::ostream& os, + const CompilationStatistics& s); + + typedef OrderedStats PhaseKindStats; + typedef std::map PhaseKindMap; + typedef std::map PhaseMap; + + TotalStats total_stats_; + PhaseKindMap phase_kind_map_; + PhaseMap phase_map_; + + DISALLOW_COPY_AND_ASSIGN(CompilationStatistics); +}; + +std::ostream& operator<<(std::ostream& os, const CompilationStatistics& s); + +} // namespace internal +} // namespace v8 + +#endif diff --git a/src/compiler/pipeline-statistics.cc b/src/compiler/pipeline-statistics.cc new file mode 100644 index 0000000000..45408b5e98 --- /dev/null +++ b/src/compiler/pipeline-statistics.cc @@ -0,0 +1,97 @@ +// 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/compiler.h" +#include "src/compiler/pipeline-statistics.h" +#include "src/compiler/zone-pool.h" + +namespace v8 { +namespace internal { +namespace compiler { + +void PipelineStatistics::CommonStats::Begin( + PipelineStatistics* pipeline_stats) { + DCHECK(scope_.is_empty()); + scope_.Reset(new ZonePool::StatsScope(pipeline_stats->zone_pool_)); + timer_.Start(); + outer_zone_initial_size_ = pipeline_stats->OuterZoneSize(); +} + + +void PipelineStatistics::CommonStats::End( + PipelineStatistics* pipeline_stats, + CompilationStatistics::BasicStats* diff) { + DCHECK(!scope_.is_empty()); + diff->function_name_ = pipeline_stats->function_name_; + diff->delta_ = timer_.Elapsed(); + size_t outer_zone_diff = + pipeline_stats->OuterZoneSize() - outer_zone_initial_size_; + diff->max_allocated_bytes_ = outer_zone_diff + scope_->GetMaxAllocatedBytes(); + diff->total_allocated_bytes_ = + outer_zone_diff + scope_->GetTotalAllocatedBytes(); + scope_.Reset(NULL); + timer_.Stop(); +} + + +PipelineStatistics::PipelineStatistics(CompilationInfo* info, + ZonePool* zone_pool) + : isolate_(info->zone()->isolate()), + outer_zone_(info->zone()), + zone_pool_(zone_pool), + compilation_stats_(isolate_->GetTurboStatistics()), + source_size_(0), + phase_kind_name_(NULL), + phase_name_(NULL) { + if (!info->shared_info().is_null()) { + source_size_ = static_cast(info->shared_info()->SourceSize()); + SmartArrayPointer name = + info->shared_info()->DebugName()->ToCString(); + function_name_ = name.get(); + } + total_stats_.Begin(this); +} + + +PipelineStatistics::~PipelineStatistics() { + if (InPhaseKind()) EndPhaseKind(); + CompilationStatistics::BasicStats diff; + total_stats_.End(this, &diff); + compilation_stats_->RecordTotalStats(source_size_, diff); +} + + +void PipelineStatistics::BeginPhaseKind(const char* phase_kind_name) { + DCHECK(!InPhase()); + if (InPhaseKind()) EndPhaseKind(); + phase_kind_name_ = phase_kind_name; + phase_kind_stats_.Begin(this); +} + + +void PipelineStatistics::EndPhaseKind() { + DCHECK(!InPhase()); + CompilationStatistics::BasicStats diff; + phase_kind_stats_.End(this, &diff); + compilation_stats_->RecordPhaseKindStats(phase_kind_name_, diff); +} + + +void PipelineStatistics::BeginPhase(const char* name) { + DCHECK(InPhaseKind()); + phase_name_ = name; + phase_stats_.Begin(this); +} + + +void PipelineStatistics::EndPhase() { + DCHECK(InPhaseKind()); + CompilationStatistics::BasicStats diff; + phase_stats_.End(this, &diff); + compilation_stats_->RecordPhaseStats(phase_kind_name_, phase_name_, diff); +} + +} // namespace compiler +} // namespace internal +} // namespace v8 diff --git a/src/compiler/pipeline-statistics.h b/src/compiler/pipeline-statistics.h new file mode 100644 index 0000000000..972a710646 --- /dev/null +++ b/src/compiler/pipeline-statistics.h @@ -0,0 +1,94 @@ +// 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. + +#ifndef V8_COMPILER_PIPELINE_STATISTICS_H_ +#define V8_COMPILER_PIPELINE_STATISTICS_H_ + +#include + +#include "src/compilation-statistics.h" +#include "src/compiler/zone-pool.h" + +namespace v8 { +namespace internal { +namespace compiler { + +class PhaseScope; + +class PipelineStatistics : public Malloced { + public: + PipelineStatistics(CompilationInfo* info, ZonePool* zone_pool); + ~PipelineStatistics(); + + void BeginPhaseKind(const char* phase_kind_name); + + private: + size_t OuterZoneSize() { + return static_cast(outer_zone_->allocation_size()); + } + + class CommonStats { + public: + CommonStats() : outer_zone_initial_size_(0) {} + + void Begin(PipelineStatistics* pipeline_stats); + void End(PipelineStatistics* pipeline_stats, + CompilationStatistics::BasicStats* diff); + + SmartPointer scope_; + base::ElapsedTimer timer_; + size_t outer_zone_initial_size_; + }; + + bool InPhaseKind() { return !phase_kind_stats_.scope_.is_empty(); } + void EndPhaseKind(); + + friend class PhaseScope; + bool InPhase() { return !phase_stats_.scope_.is_empty(); } + void BeginPhase(const char* name); + void EndPhase(); + + Isolate* isolate_; + Zone* outer_zone_; + ZonePool* zone_pool_; + CompilationStatistics* compilation_stats_; + std::string function_name_; + + // Stats for the entire compilation. + CommonStats total_stats_; + size_t source_size_; + + // Stats for phase kind. + const char* phase_kind_name_; + CommonStats phase_kind_stats_; + + // Stats for phase. + const char* phase_name_; + CommonStats phase_stats_; + + DISALLOW_COPY_AND_ASSIGN(PipelineStatistics); +}; + + +class PhaseScope { + public: + PhaseScope(PipelineStatistics* pipeline_stats, const char* name) + : pipeline_stats_(pipeline_stats) { + if (pipeline_stats_ != NULL) pipeline_stats_->BeginPhase(name); + } + ~PhaseScope() { + if (pipeline_stats_ != NULL) pipeline_stats_->EndPhase(); + } + + private: + PipelineStatistics* const pipeline_stats_; + + DISALLOW_COPY_AND_ASSIGN(PhaseScope); +}; + +} // namespace compiler +} // namespace internal +} // namespace v8 + +#endif diff --git a/src/compiler/pipeline.cc b/src/compiler/pipeline.cc index 438f104595..609de0670b 100644 --- a/src/compiler/pipeline.cc +++ b/src/compiler/pipeline.cc @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "src/compiler/pipeline.h" - #include #include "src/base/platform/elapsed-timer.h" @@ -22,6 +20,8 @@ #include "src/compiler/js-typed-lowering.h" #include "src/compiler/machine-operator-reducer.h" #include "src/compiler/phi-reducer.h" +#include "src/compiler/pipeline.h" +#include "src/compiler/pipeline-statistics.h" #include "src/compiler/register-allocator.h" #include "src/compiler/schedule.h" #include "src/compiler/scheduler.h" @@ -31,7 +31,6 @@ #include "src/compiler/value-numbering-reducer.h" #include "src/compiler/verifier.h" #include "src/compiler/zone-pool.h" -#include "src/hydrogen.h" #include "src/ostreams.h" #include "src/utils.h" @@ -39,55 +38,6 @@ namespace v8 { namespace internal { namespace compiler { -class PhaseStats { - public: - enum PhaseKind { CREATE_GRAPH, OPTIMIZATION, CODEGEN }; - - PhaseStats(CompilationInfo* info, ZonePool* zone_pool, PhaseKind kind, - const char* name) - : info_(info), - stats_scope_(zone_pool), - kind_(kind), - name_(name), - size_(0) { - if (FLAG_turbo_stats) { - timer_.Start(); - size_ = info_->zone()->allocation_size(); - } - } - - ~PhaseStats() { - if (FLAG_turbo_stats) { - base::TimeDelta delta = timer_.Elapsed(); - size_t bytes = info_->zone()->allocation_size() + - stats_scope_.GetMaxAllocatedBytes() - size_; - HStatistics* stats = info_->isolate()->GetTStatistics(); - stats->SaveTiming(name_, delta, static_cast(bytes)); - - switch (kind_) { - case CREATE_GRAPH: - stats->IncrementCreateGraph(delta); - break; - case OPTIMIZATION: - stats->IncrementOptimizeGraph(delta); - break; - case CODEGEN: - stats->IncrementGenerateCode(delta); - break; - } - } - } - - private: - CompilationInfo* info_; - ZonePool::StatsScope stats_scope_; - PhaseKind kind_; - const char* name_; - size_t size_; - base::ElapsedTimer timer_; -}; - - static inline bool VerifyGraphs() { #ifdef DEBUG return true; @@ -226,7 +176,13 @@ Handle Pipeline::GenerateCode() { return Handle::null(); } - if (FLAG_turbo_stats) isolate()->GetTStatistics()->Initialize(info_); + ZonePool zone_pool(isolate()); + + SmartPointer pipeline_statistics; + if (FLAG_turbo_stats) { + pipeline_statistics.Reset(new PipelineStatistics(info(), &zone_pool)); + pipeline_statistics->BeginPhaseKind("create graph"); + } if (FLAG_trace_turbo) { OFStream os(stdout); @@ -237,8 +193,6 @@ Handle Pipeline::GenerateCode() { PrintCompilationStart(); } - ZonePool zone_pool(isolate()); - // Build the graph. Graph graph(zone()); SourcePositionTable source_positions(&graph); @@ -253,8 +207,7 @@ Handle Pipeline::GenerateCode() { JSGraph jsgraph(&graph, &common, &javascript, &machine); Node* context_node; { - PhaseStats graph_builder_stats(info(), &zone_pool, PhaseStats::CREATE_GRAPH, - "graph builder"); + PhaseScope phase_scope(pipeline_statistics.get(), "graph builder"); ZonePool::Scope zone_scope(&zone_pool); AstGraphBuilderWithPositions graph_builder(zone_scope.zone(), info(), &jsgraph, &source_positions); @@ -262,8 +215,7 @@ Handle Pipeline::GenerateCode() { context_node = graph_builder.GetFunctionContext(); } { - PhaseStats phi_reducer_stats(info(), &zone_pool, PhaseStats::CREATE_GRAPH, - "phi reduction"); + PhaseScope phase_scope(pipeline_statistics.get(), "phi reduction"); PhiReducer phi_reducer; GraphReducer graph_reducer(&graph); graph_reducer.AddReducer(&phi_reducer); @@ -285,6 +237,7 @@ Handle Pipeline::GenerateCode() { } if (info()->is_inlining_enabled()) { + PhaseScope phase_scope(pipeline_statistics.get(), "inlining"); SourcePositionTable::Scope pos(&source_positions, SourcePosition::Unknown()); ZonePool::Scope zone_scope(&zone_pool); @@ -304,15 +257,20 @@ Handle Pipeline::GenerateCode() { if (info()->is_typing_enabled()) { { // Type the graph. - PhaseStats typer_stats(info(), &zone_pool, PhaseStats::CREATE_GRAPH, - "typer"); + PhaseScope phase_scope(pipeline_statistics.get(), "typer"); typer.Run(); VerifyAndPrintGraph(&graph, "Typed"); } + } + + if (!pipeline_statistics.is_empty()) { + pipeline_statistics->BeginPhaseKind("lowering"); + } + + if (info()->is_typing_enabled()) { { // Lower JSOperators where we can determine types. - PhaseStats lowering_stats(info(), &zone_pool, PhaseStats::CREATE_GRAPH, - "typed lowering"); + PhaseScope phase_scope(pipeline_statistics.get(), "typed lowering"); SourcePositionTable::Scope pos(&source_positions, SourcePosition::Unknown()); ValueNumberingReducer vn_reducer(zone()); @@ -328,8 +286,7 @@ Handle Pipeline::GenerateCode() { } { // Lower simplified operators and insert changes. - PhaseStats lowering_stats(info(), &zone_pool, PhaseStats::CREATE_GRAPH, - "simplified lowering"); + PhaseScope phase_scope(pipeline_statistics.get(), "simplified lowering"); SourcePositionTable::Scope pos(&source_positions, SourcePosition::Unknown()); SimplifiedLowering lowering(&jsgraph); @@ -345,8 +302,7 @@ Handle Pipeline::GenerateCode() { } { // Lower changes that have been inserted before. - PhaseStats lowering_stats(info(), &zone_pool, PhaseStats::OPTIMIZATION, - "change lowering"); + PhaseScope phase_scope(pipeline_statistics.get(), "change lowering"); SourcePositionTable::Scope pos(&source_positions, SourcePosition::Unknown()); Linkage linkage(info()); @@ -367,10 +323,9 @@ Handle Pipeline::GenerateCode() { } { + PhaseScope phase_scope(pipeline_statistics.get(), "control reduction"); SourcePositionTable::Scope pos(&source_positions, SourcePosition::Unknown()); - PhaseStats control_reducer_stats( - info(), &zone_pool, PhaseStats::CREATE_GRAPH, "control reduction"); ZonePool::Scope zone_scope(&zone_pool); ControlReducer::ReduceGraph(zone_scope.zone(), &jsgraph, &common); @@ -380,8 +335,7 @@ Handle Pipeline::GenerateCode() { { // Lower any remaining generic JSOperators. - PhaseStats lowering_stats(info(), &zone_pool, PhaseStats::CREATE_GRAPH, - "generic lowering"); + PhaseScope phase_scope(pipeline_statistics.get(), "generic lowering"); SourcePositionTable::Scope pos(&source_positions, SourcePosition::Unknown()); JSGenericLowering lowering(info(), &jsgraph); @@ -393,18 +347,25 @@ Handle Pipeline::GenerateCode() { VerifyAndPrintGraph(&graph, "Lowered generic", true); } + if (!pipeline_statistics.is_empty()) { + pipeline_statistics->BeginPhaseKind("code generation"); + } + source_positions.RemoveDecorator(); + Schedule* schedule; + { + PhaseScope phase_scope(pipeline_statistics.get(), "scheduling"); + // Compute a schedule. + schedule = ComputeSchedule(&zone_pool, &graph); + } + Handle code = Handle::null(); { - // Compute a schedule. - Schedule* schedule = ComputeSchedule(&zone_pool, &graph); // Generate optimized code. - PhaseStats codegen_stats(info(), &zone_pool, PhaseStats::CODEGEN, - "codegen"); Linkage linkage(info()); - code = - GenerateCode(&zone_pool, &linkage, &graph, schedule, &source_positions); + code = GenerateCode(pipeline_statistics.get(), &zone_pool, &linkage, &graph, + schedule, &source_positions); info()->SetCode(code); } @@ -424,8 +385,6 @@ Handle Pipeline::GenerateCode() { Schedule* Pipeline::ComputeSchedule(ZonePool* zone_pool, Graph* graph) { - PhaseStats schedule_stats(info(), zone_pool, PhaseStats::CODEGEN, - "scheduling"); Schedule* schedule = Scheduler::ComputeSchedule(zone_pool, graph); TraceSchedule(schedule); if (VerifyGraphs()) ScheduleVerifier::Run(schedule); @@ -446,8 +405,8 @@ Handle Pipeline::GenerateCodeForMachineGraph(Linkage* linkage, TraceSchedule(schedule); SourcePositionTable source_positions(graph); - Handle code = - GenerateCode(&zone_pool, linkage, graph, schedule, &source_positions); + Handle code = GenerateCode(NULL, &zone_pool, linkage, graph, schedule, + &source_positions); #if ENABLE_DISASSEMBLER if (!code.is_null() && FLAG_print_opt_code) { CodeTracer::Scope tracing_scope(isolate()->GetCodeTracer()); @@ -459,7 +418,8 @@ Handle Pipeline::GenerateCodeForMachineGraph(Linkage* linkage, } -Handle Pipeline::GenerateCode(ZonePool* zone_pool, Linkage* linkage, +Handle Pipeline::GenerateCode(PipelineStatistics* pipeline_statistics, + ZonePool* zone_pool, Linkage* linkage, Graph* graph, Schedule* schedule, SourcePositionTable* source_positions) { DCHECK_NOT_NULL(graph); @@ -477,6 +437,7 @@ Handle Pipeline::GenerateCode(ZonePool* zone_pool, Linkage* linkage, // Select and schedule instructions covering the scheduled graph. { + PhaseScope phase_scope(pipeline_statistics, "select instructions"); ZonePool::Scope zone_scope(zone_pool); InstructionSelector selector(zone_scope.zone(), linkage, &sequence, schedule, source_positions); @@ -502,7 +463,7 @@ Handle Pipeline::GenerateCode(ZonePool* zone_pool, Linkage* linkage, ZonePool::Scope zone_scope(zone_pool); RegisterAllocator allocator(zone_scope.zone(), &frame, linkage->info(), &sequence); - if (!allocator.Allocate(zone_pool)) { + if (!allocator.Allocate(pipeline_statistics)) { linkage->info()->AbortOptimization(kNotEnoughVirtualRegistersRegalloc); return Handle::null(); } @@ -518,8 +479,12 @@ Handle Pipeline::GenerateCode(ZonePool* zone_pool, Linkage* linkage, } // Generate native sequence. - CodeGenerator generator(&frame, linkage, &sequence); - Handle code = generator.GenerateCode(); + Handle code; + { + PhaseScope phase_scope(pipeline_statistics, "generate code"); + CodeGenerator generator(&frame, linkage, &sequence); + code = generator.GenerateCode(); + } if (profiler_data != NULL) { #if ENABLE_DISASSEMBLER std::ostringstream os; diff --git a/src/compiler/pipeline.h b/src/compiler/pipeline.h index 87c22a6aeb..5bd0e615b8 100644 --- a/src/compiler/pipeline.h +++ b/src/compiler/pipeline.h @@ -22,6 +22,7 @@ namespace compiler { class Graph; class InstructionSequence; class Linkage; +class PipelineStatistics; class RegisterAllocator; class Schedule; class SourcePositionTable; @@ -61,7 +62,8 @@ class Pipeline { void PrintAllocator(const char* phase, const RegisterAllocator* allocator); void VerifyAndPrintGraph(Graph* graph, const char* phase, bool untyped = false); - Handle GenerateCode(ZonePool* zone_pool, Linkage* linkage, Graph* graph, + Handle GenerateCode(PipelineStatistics* pipeline_statistics, + ZonePool* zone_pool, Linkage* linkage, Graph* graph, Schedule* schedule, SourcePositionTable* source_positions); }; diff --git a/src/compiler/register-allocator.cc b/src/compiler/register-allocator.cc index 2a5f8550e9..c09f6056c7 100644 --- a/src/compiler/register-allocator.cc +++ b/src/compiler/register-allocator.cc @@ -2,10 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "src/compiler/register-allocator.h" - #include "src/compiler/linkage.h" -#include "src/hydrogen.h" +#include "src/compiler/pipeline-statistics.h" +#include "src/compiler/register-allocator.h" #include "src/string-stream.h" namespace v8 { @@ -503,7 +502,6 @@ RegisterAllocator::RegisterAllocator(Zone* local_zone, Frame* frame, CompilationInfo* info, InstructionSequence* code) : zone_(local_zone), - zone_pool_(NULL), frame_(frame), info_(info), code_(code), @@ -1096,72 +1094,53 @@ void RegisterAllocator::ResolvePhis(const InstructionBlock* block) { } -bool RegisterAllocator::Allocate(ZonePool* zone_pool) { - DCHECK_EQ(NULL, zone_pool_); - zone_pool_ = zone_pool; +bool RegisterAllocator::Allocate(PipelineStatistics* stats) { assigned_registers_ = new (code_zone()) BitVector(Register::NumAllocatableRegisters(), code_zone()); assigned_double_registers_ = new (code_zone()) BitVector(DoubleRegister::NumAllocatableAliasedRegisters(), code_zone()); - MeetRegisterConstraints(); + { + PhaseScope phase_scope(stats, "meet register constraints"); + MeetRegisterConstraints(); + } if (!AllocationOk()) return false; - ResolvePhis(); - BuildLiveRanges(); - AllocateGeneralRegisters(); + { + PhaseScope phase_scope(stats, "resolve phis"); + ResolvePhis(); + } + { + PhaseScope phase_scope(stats, "build live ranges"); + BuildLiveRanges(); + } + { + PhaseScope phase_scope(stats, "allocate general registers"); + AllocateGeneralRegisters(); + } if (!AllocationOk()) return false; - AllocateDoubleRegisters(); + { + PhaseScope phase_scope(stats, "allocate double registers"); + AllocateDoubleRegisters(); + } if (!AllocationOk()) return false; - PopulatePointerMaps(); - ConnectRanges(); - ResolveControlFlow(); + { + PhaseScope phase_scope(stats, "populate pointer maps"); + PopulatePointerMaps(); + } + { + PhaseScope phase_scope(stats, "connect ranges"); + ConnectRanges(); + } + { + PhaseScope phase_scope(stats, "resolve control flow"); + ResolveControlFlow(); + } frame()->SetAllocatedRegisters(assigned_registers_); frame()->SetAllocatedDoubleRegisters(assigned_double_registers_); return true; } -class RegisterAllocatorPhase : public CompilationPhase { - public: - RegisterAllocatorPhase(const char* name, RegisterAllocator* allocator) - : CompilationPhase(name, allocator->info()), - allocator_(allocator), - allocator_zone_start_allocation_size_(0), - stats_(NULL) { - if (FLAG_turbo_stats) { - allocator_zone_start_allocation_size_ = - allocator->info()->zone()->allocation_size(); - if (allocator->zone_pool() != NULL) { - stats_ = new ZonePool::StatsScope(allocator->zone_pool()); - } - } - } - - ~RegisterAllocatorPhase() { - if (FLAG_turbo_stats) { - unsigned size = allocator_->info()->zone()->allocation_size() - - allocator_zone_start_allocation_size_; - if (stats_ != NULL) { - size += static_cast(stats_->GetMaxAllocatedBytes()); - } - isolate()->GetTStatistics()->SaveTiming(name(), base::TimeDelta(), size); - } - delete stats_; -#ifdef DEBUG - if (allocator_ != NULL) allocator_->Verify(); -#endif - } - - private: - RegisterAllocator* allocator_; - unsigned allocator_zone_start_allocation_size_; - ZonePool::StatsScope* stats_; - - DISALLOW_COPY_AND_ASSIGN(RegisterAllocatorPhase); -}; - - void RegisterAllocator::MeetRegisterConstraints() { - RegisterAllocatorPhase phase("L_Register constraints", this); for (int i = 0; i < code()->InstructionBlockCount(); ++i) { MeetRegisterConstraints( code()->InstructionBlockAt(BasicBlock::RpoNumber::FromInt(i))); @@ -1171,8 +1150,6 @@ void RegisterAllocator::MeetRegisterConstraints() { void RegisterAllocator::ResolvePhis() { - RegisterAllocatorPhase phase("L_Resolve phis", this); - // Process the blocks in reverse order. for (int i = code()->InstructionBlockCount() - 1; i >= 0; --i) { ResolvePhis(code()->InstructionBlockAt(BasicBlock::RpoNumber::FromInt(i))); @@ -1250,7 +1227,6 @@ const InstructionBlock* RegisterAllocator::GetInstructionBlock( void RegisterAllocator::ConnectRanges() { - RegisterAllocatorPhase phase("L_Connect ranges", this); for (int i = 0; i < live_ranges()->length(); ++i) { LiveRange* first_range = live_ranges()->at(i); if (first_range == NULL || first_range->parent() != NULL) continue; @@ -1294,7 +1270,6 @@ bool RegisterAllocator::CanEagerlyResolveControlFlow( void RegisterAllocator::ResolveControlFlow() { - RegisterAllocatorPhase phase("L_Resolve control flow", this); for (int block_id = 1; block_id < code()->InstructionBlockCount(); ++block_id) { const InstructionBlock* block = @@ -1316,7 +1291,6 @@ void RegisterAllocator::ResolveControlFlow() { void RegisterAllocator::BuildLiveRanges() { - RegisterAllocatorPhase phase("L_Build live ranges", this); InitializeLivenessAnalysis(); // Process the blocks in reverse order. for (int block_id = code()->InstructionBlockCount() - 1; block_id >= 0; @@ -1457,8 +1431,6 @@ bool RegisterAllocator::SafePointsAreInOrder() const { void RegisterAllocator::PopulatePointerMaps() { - RegisterAllocatorPhase phase("L_Populate pointer maps", this); - DCHECK(SafePointsAreInOrder()); // Iterate over all safe point positions and record a pointer @@ -1541,7 +1513,6 @@ void RegisterAllocator::PopulatePointerMaps() { void RegisterAllocator::AllocateGeneralRegisters() { - RegisterAllocatorPhase phase("L_Allocate general registers", this); num_registers_ = Register::NumAllocatableRegisters(); mode_ = GENERAL_REGISTERS; AllocateRegisters(); @@ -1549,7 +1520,6 @@ void RegisterAllocator::AllocateGeneralRegisters() { void RegisterAllocator::AllocateDoubleRegisters() { - RegisterAllocatorPhase phase("L_Allocate double registers", this); num_registers_ = DoubleRegister::NumAllocatableAliasedRegisters(); mode_ = DOUBLE_REGISTERS; AllocateRegisters(); diff --git a/src/compiler/register-allocator.h b/src/compiler/register-allocator.h index 60d08433af..3b6656ee8f 100644 --- a/src/compiler/register-allocator.h +++ b/src/compiler/register-allocator.h @@ -23,6 +23,8 @@ class PointerMap; namespace compiler { +class PipelineStatistics; + enum RegisterKind { UNALLOCATED_REGISTERS, GENERAL_REGISTERS, @@ -331,8 +333,7 @@ class RegisterAllocator BASE_EMBEDDED { // Returns the register kind required by the given virtual register. RegisterKind RequiredRegisterKind(int virtual_register) const; - // TODO(dcarney): fix compilation phase stats to not require this. - bool Allocate(ZonePool* zone_pool = NULL); + bool Allocate(PipelineStatistics* stats = NULL); const ZoneList* live_ranges() const { return &live_ranges_; } const Vector* fixed_live_ranges() const { @@ -344,7 +345,6 @@ class RegisterAllocator BASE_EMBEDDED { CompilationInfo* info() const { return info_; } inline InstructionSequence* code() const { return code_; } - ZonePool* zone_pool() const { return zone_pool_; } // This zone is for datastructures only needed during register allocation. inline Zone* zone() const { return zone_; } @@ -501,8 +501,6 @@ class RegisterAllocator BASE_EMBEDDED { Frame* frame() const { return frame_; } Zone* const zone_; - // TODO(dcarney): remove this. - ZonePool* zone_pool_; Frame* const frame_; CompilationInfo* const info_; InstructionSequence* const code_; diff --git a/src/compiler/zone-pool.cc b/src/compiler/zone-pool.cc index 8276023522..179988d418 100644 --- a/src/compiler/zone-pool.cc +++ b/src/compiler/zone-pool.cc @@ -9,7 +9,9 @@ namespace internal { namespace compiler { ZonePool::StatsScope::StatsScope(ZonePool* zone_pool) - : zone_pool_(zone_pool), max_allocated_bytes_(0) { + : zone_pool_(zone_pool), + total_allocated_bytes_at_start_(zone_pool->GetTotalAllocatedBytes()), + max_allocated_bytes_(0) { zone_pool_->stats_.push_back(this); for (auto zone : zone_pool_->used_) { size_t size = static_cast(zone->allocation_size()); @@ -46,6 +48,11 @@ size_t ZonePool::StatsScope::GetCurrentAllocatedBytes() { } +size_t ZonePool::StatsScope::GetTotalAllocatedBytes() { + return zone_pool_->GetTotalAllocatedBytes() - total_allocated_bytes_at_start_; +} + + void ZonePool::StatsScope::ZoneReturned(Zone* zone) { size_t current_total = GetCurrentAllocatedBytes(); // Update max. diff --git a/src/compiler/zone-pool.h b/src/compiler/zone-pool.h index 648ab5a314..8b43265c1a 100644 --- a/src/compiler/zone-pool.h +++ b/src/compiler/zone-pool.h @@ -44,6 +44,7 @@ class ZonePool FINAL { size_t GetMaxAllocatedBytes(); size_t GetCurrentAllocatedBytes(); + size_t GetTotalAllocatedBytes(); private: friend class ZonePool; @@ -53,6 +54,7 @@ class ZonePool FINAL { ZonePool* const zone_pool_; InitialValues initial_values_; + size_t total_allocated_bytes_at_start_; size_t max_allocated_bytes_; DISALLOW_COPY_AND_ASSIGN(StatsScope); diff --git a/src/hydrogen.cc b/src/hydrogen.cc index 92e1d2a034..c2bdd6609b 100644 --- a/src/hydrogen.cc +++ b/src/hydrogen.cc @@ -12594,15 +12594,14 @@ void HStatistics::Initialize(CompilationInfo* info) { } -void HStatistics::Print(const char* stats_name) { +void HStatistics::Print() { PrintF( "\n" "----------------------------------------" "----------------------------------------\n" - "--- %s timing results:\n" + "--- Hydrogen timing results:\n" "----------------------------------------" - "----------------------------------------\n", - stats_name); + "----------------------------------------\n"); base::TimeDelta sum; for (int i = 0; i < times_.length(); ++i) { sum += times_[i]; diff --git a/src/hydrogen.h b/src/hydrogen.h index 9e3601ac8d..0ff5a45e59 100644 --- a/src/hydrogen.h +++ b/src/hydrogen.h @@ -2760,7 +2760,7 @@ class HStatistics FINAL: public Malloced { source_size_(0) { } void Initialize(CompilationInfo* info); - void Print(const char* stats_name); + void Print(); void SaveTiming(const char* name, base::TimeDelta time, unsigned size); void IncrementFullCodeGen(base::TimeDelta full_code_gen) { diff --git a/src/isolate.cc b/src/isolate.cc index 7be7756192..849ab8ced5 100644 --- a/src/isolate.cc +++ b/src/isolate.cc @@ -5,6 +5,7 @@ #include #include // NOLINT(readability/streams) +#include // NOLINT(readability/streams) #include "src/v8.h" @@ -16,6 +17,7 @@ #include "src/bootstrapper.h" #include "src/codegen.h" #include "src/compilation-cache.h" +#include "src/compilation-statistics.h" #include "src/cpu-profiler.h" #include "src/debug.h" #include "src/deoptimizer.h" @@ -1606,8 +1608,10 @@ void Isolate::Deinit() { heap_.mark_compact_collector()->EnsureSweepingCompleted(); } - if (FLAG_turbo_stats) GetTStatistics()->Print("TurboFan"); - if (FLAG_hydrogen_stats) GetHStatistics()->Print("Hydrogen"); + if (turbo_statistics() != NULL) { + std::cout << *turbo_statistics() << std::endl; + } + if (FLAG_hydrogen_stats) GetHStatistics()->Print(); if (FLAG_print_deopt_stress) { PrintF(stdout, "=== Stress deopt counter: %u\n", stress_deopt_count_); @@ -2119,9 +2123,10 @@ HStatistics* Isolate::GetHStatistics() { } -HStatistics* Isolate::GetTStatistics() { - if (tstatistics() == NULL) set_tstatistics(new HStatistics()); - return tstatistics(); +CompilationStatistics* Isolate::GetTurboStatistics() { + if (turbo_statistics() == NULL) + set_turbo_statistics(new CompilationStatistics()); + return turbo_statistics(); } diff --git a/src/isolate.h b/src/isolate.h index 55d139c57a..0ede08f60d 100644 --- a/src/isolate.h +++ b/src/isolate.h @@ -40,6 +40,7 @@ class CodeRange; class CodeStubDescriptor; class CodeTracer; class CompilationCache; +class CompilationStatistics; class ContextSlotCache; class Counters; class CpuFeatures; @@ -383,7 +384,7 @@ typedef List DebugObjectCache; V(int, pending_microtask_count, 0) \ V(bool, autorun_microtasks, true) \ V(HStatistics*, hstatistics, NULL) \ - V(HStatistics*, tstatistics, NULL) \ + V(CompilationStatistics*, turbo_statistics, NULL) \ V(HTracer*, htracer, NULL) \ V(CodeTracer*, code_tracer, NULL) \ V(bool, fp_stubs_generated, false) \ @@ -1052,7 +1053,7 @@ class Isolate { int id() const { return static_cast(id_); } HStatistics* GetHStatistics(); - HStatistics* GetTStatistics(); + CompilationStatistics* GetTurboStatistics(); HTracer* GetHTracer(); CodeTracer* GetCodeTracer(); diff --git a/test/unittests/compiler/zone-pool-unittest.cc b/test/unittests/compiler/zone-pool-unittest.cc index 5bc0bbd101..e23557adc4 100644 --- a/test/unittests/compiler/zone-pool-unittest.cc +++ b/test/unittests/compiler/zone-pool-unittest.cc @@ -23,9 +23,11 @@ class ZonePoolTest : public TestWithIsolate { ASSERT_EQ(total, zone_pool()->GetTotalAllocatedBytes()); } - void Expect(ZonePool::StatsScope* stats, size_t current, size_t max) { + void Expect(ZonePool::StatsScope* stats, size_t current, size_t max, + size_t total) { ASSERT_EQ(current, stats->GetCurrentAllocatedBytes()); ASSERT_EQ(max, stats->GetMaxAllocatedBytes()); + ASSERT_EQ(total, stats->GetTotalAllocatedBytes()); } size_t Allocate(Zone* zone) { @@ -45,7 +47,7 @@ TEST_F(ZonePoolTest, Empty) { ExpectForPool(0, 0, 0); { ZonePool::StatsScope stats(zone_pool()); - Expect(&stats, 0, 0); + Expect(&stats, 0, 0, 0); } ExpectForPool(0, 0, 0); { @@ -77,7 +79,7 @@ TEST_F(ZonePoolTest, MultipleZonesWithDeletion) { before_deletion += Allocate(scopes[i]->zone()); // Add some stuff. } - Expect(&stats, before_deletion, before_deletion); + Expect(&stats, before_deletion, before_deletion, before_deletion); ExpectForPool(before_stats + before_deletion, before_stats + before_deletion, before_stats + before_deletion); @@ -87,7 +89,7 @@ TEST_F(ZonePoolTest, MultipleZonesWithDeletion) { scopes[i] = new ZonePool::Scope(zone_pool()); } - Expect(&stats, 0, before_deletion); + Expect(&stats, 0, before_deletion, before_deletion); ExpectForPool(0, before_stats + before_deletion, before_stats + before_deletion); @@ -96,7 +98,8 @@ TEST_F(ZonePoolTest, MultipleZonesWithDeletion) { after_deletion += Allocate(scopes[i]->zone()); // Add some stuff. } - Expect(&stats, after_deletion, std::max(after_deletion, before_deletion)); + Expect(&stats, after_deletion, std::max(after_deletion, before_deletion), + before_deletion + after_deletion); ExpectForPool(after_deletion, std::max(after_deletion, before_stats + before_deletion), before_stats + before_deletion + after_deletion); @@ -106,7 +109,8 @@ TEST_F(ZonePoolTest, MultipleZonesWithDeletion) { delete scopes[i]; } - Expect(&stats, 0, std::max(after_deletion, before_deletion)); + Expect(&stats, 0, std::max(after_deletion, before_deletion), + before_deletion + after_deletion); ExpectForPool(0, std::max(after_deletion, before_stats + before_deletion), before_stats + before_deletion + after_deletion); } @@ -136,19 +140,20 @@ TEST_F(ZonePoolTest, SimpleAllocationLoop) { total_allocated += bytes; max_loop_allocation = std::max(max_loop_allocation, outer_allocated + allocated); - Expect(&inner_stats, allocated, allocated); - Expect(&outer_stats, outer_allocated + allocated, - max_loop_allocation); + Expect(&inner_stats, allocated, allocated, allocated); + Expect(&outer_stats, outer_allocated + allocated, max_loop_allocation, + total_allocated); ExpectForPool(outer_allocated + allocated, max_loop_allocation, total_allocated); } } - Expect(&inner_stats, 0, allocated); - Expect(&outer_stats, outer_allocated, max_loop_allocation); + Expect(&inner_stats, 0, allocated, allocated); + Expect(&outer_stats, outer_allocated, max_loop_allocation, + total_allocated); ExpectForPool(outer_allocated, max_loop_allocation, total_allocated); } } - Expect(&outer_stats, 0, max_loop_allocation); + Expect(&outer_stats, 0, max_loop_allocation, total_allocated); ExpectForPool(0, max_loop_allocation, total_allocated); } diff --git a/tools/gyp/v8.gyp b/tools/gyp/v8.gyp index 9bf3a9579a..72437d8ce1 100644 --- a/tools/gyp/v8.gyp +++ b/tools/gyp/v8.gyp @@ -387,6 +387,8 @@ '../../src/codegen.h', '../../src/compilation-cache.cc', '../../src/compilation-cache.h', + '../../src/compilation-statistics.cc', + '../../src/compilation-statistics.h', '../../src/compiler/access-builder.cc', '../../src/compiler/access-builder.h', '../../src/compiler/ast-graph-builder.cc', @@ -473,6 +475,8 @@ '../../src/compiler/phi-reducer.h', '../../src/compiler/pipeline.cc', '../../src/compiler/pipeline.h', + '../../src/compiler/pipeline-statistics.cc', + '../../src/compiler/pipeline-statistics.h', '../../src/compiler/raw-machine-assembler.cc', '../../src/compiler/raw-machine-assembler.h', '../../src/compiler/register-allocator.cc',