Commit 5d0e9a24 authored by dcarney@chromium.org's avatar dcarney@chromium.org

[turbofan] basic block profiler

R=titzer@chromium.org, mstarzinger@chromium.org

BUG=

Review URL: https://codereview.chromium.org/593563005

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@24263 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 667f15a1
......@@ -440,6 +440,8 @@ source_set("v8_base") {
"src/background-parsing-task.h",
"src/bailout-reason.cc",
"src/bailout-reason.h",
"src/basic-block-profiler.cc",
"src/basic-block-profiler.h",
"src/bignum-dtoa.cc",
"src/bignum-dtoa.h",
"src/bignum.cc",
......@@ -471,6 +473,8 @@ source_set("v8_base") {
"src/compiler/access-builder.h",
"src/compiler/ast-graph-builder.cc",
"src/compiler/ast-graph-builder.h",
"src/compiler/basic-block-instrumentor.cc",
"src/compiler/basic-block-instrumentor.h",
"src/compiler/change-lowering.cc",
"src/compiler/change-lowering.h",
"src/compiler/code-generator-impl.h",
......
// 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/basic-block-profiler.h"
namespace v8 {
namespace internal {
BasicBlockProfiler::Data::Data(size_t n_blocks)
: n_blocks_(n_blocks), block_ids_(n_blocks_, -1), counts_(n_blocks_, 0) {}
BasicBlockProfiler::Data::~Data() {}
static void InsertIntoString(OStringStream* os, std::string* string) {
string->insert(string->begin(), os->c_str(), &os->c_str()[os->size()]);
}
void BasicBlockProfiler::Data::SetCode(OStringStream* os) {
InsertIntoString(os, &code_);
}
void BasicBlockProfiler::Data::SetFunctionName(OStringStream* os) {
InsertIntoString(os, &function_name_);
}
void BasicBlockProfiler::Data::SetSchedule(OStringStream* os) {
InsertIntoString(os, &schedule_);
}
void BasicBlockProfiler::Data::SetBlockId(size_t offset, int block_id) {
DCHECK(offset < n_blocks_);
block_ids_[offset] = block_id;
}
uint32_t* BasicBlockProfiler::Data::GetCounterAddress(size_t offset) {
DCHECK(offset < n_blocks_);
return &counts_[offset];
}
void BasicBlockProfiler::Data::ResetCounts() {
for (size_t i = 0; i < n_blocks_; ++i) {
counts_[i] = 0;
}
}
BasicBlockProfiler::BasicBlockProfiler() {}
BasicBlockProfiler::Data* BasicBlockProfiler::NewData(size_t n_blocks) {
Data* data = new Data(n_blocks);
data_list_.push_back(data);
return data;
}
BasicBlockProfiler::~BasicBlockProfiler() {
for (DataList::iterator i = data_list_.begin(); i != data_list_.end(); ++i) {
delete (*i);
}
}
void BasicBlockProfiler::ResetCounts() {
for (DataList::iterator i = data_list_.begin(); i != data_list_.end(); ++i) {
(*i)->ResetCounts();
}
}
OStream& operator<<(OStream& os, const BasicBlockProfiler& p) {
os << "---- Start Profiling Data ----" << endl;
typedef BasicBlockProfiler::DataList::const_iterator iterator;
for (iterator i = p.data_list_.begin(); i != p.data_list_.end(); ++i) {
os << **i;
}
os << "---- End Profiling Data ----" << endl;
return os;
}
OStream& operator<<(OStream& os, const BasicBlockProfiler::Data& d) {
const char* name = "unknown function";
if (!d.function_name_.empty()) {
name = d.function_name_.c_str();
}
if (!d.schedule_.empty()) {
os << "schedule for " << name << endl;
os << d.schedule_.c_str() << endl;
}
os << "block counts for " << name << ":" << endl;
for (size_t i = 0; i < d.n_blocks_; ++i) {
os << "block " << d.block_ids_[i] << " : " << d.counts_[i] << endl;
}
os << endl;
if (!d.code_.empty()) {
os << d.code_.c_str() << endl;
}
return os;
}
} // namespace internal
} // namespace v8
// 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_BASIC_BLOCK_PROFILER_H_
#define V8_BASIC_BLOCK_PROFILER_H_
#include <list>
#include "src/v8.h"
namespace v8 {
namespace internal {
class Schedule;
class Graph;
class BasicBlockProfiler {
public:
class Data {
public:
size_t n_blocks() const { return n_blocks_; }
const uint32_t* counts() const { return &counts_[0]; }
void SetCode(OStringStream* os);
void SetFunctionName(OStringStream* os);
void SetSchedule(OStringStream* os);
void SetBlockId(size_t offset, int block_id);
uint32_t* GetCounterAddress(size_t offset);
private:
friend class BasicBlockProfiler;
friend OStream& operator<<(OStream& os, const BasicBlockProfiler::Data& s);
explicit Data(size_t n_blocks);
~Data();
void ResetCounts();
const size_t n_blocks_;
std::vector<int> block_ids_;
std::vector<uint32_t> counts_;
std::string function_name_;
std::string schedule_;
std::string code_;
DISALLOW_COPY_AND_ASSIGN(Data);
};
typedef std::list<Data*> DataList;
BasicBlockProfiler();
~BasicBlockProfiler();
Data* NewData(size_t n_blocks);
void ResetCounts();
const DataList* data_list() { return &data_list_; }
private:
friend OStream& operator<<(OStream& os, const BasicBlockProfiler& s);
DataList data_list_;
DISALLOW_COPY_AND_ASSIGN(BasicBlockProfiler);
};
OStream& operator<<(OStream& os, const BasicBlockProfiler& s);
OStream& operator<<(OStream& os, const BasicBlockProfiler::Data& s);
} // namespace internal
} // namespace v8
#endif // V8_BASIC_BLOCK_PROFILER_H_
// 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/basic-block-instrumentor.h"
#include "src/compiler/common-operator.h"
#include "src/compiler/graph.h"
#include "src/compiler/machine-operator.h"
#include "src/compiler/operator-properties-inl.h"
#include "src/compiler/schedule.h"
namespace v8 {
namespace internal {
namespace compiler {
// Find the first place to insert new nodes in a block that's already been
// scheduled that won't upset the register allocator.
static NodeVector::iterator FindInsertionPoint(NodeVector* nodes) {
NodeVector::iterator i = nodes->begin();
for (; i != nodes->end(); ++i) {
const Operator* op = (*i)->op();
if (OperatorProperties::IsBasicBlockBegin(op)) continue;
switch (op->opcode()) {
case IrOpcode::kParameter:
case IrOpcode::kPhi:
case IrOpcode::kEffectPhi:
continue;
}
break;
}
return i;
}
// TODO(dcarney): need to mark code as non-serializable.
static const Operator* PointerConstant(CommonOperatorBuilder* common,
void* ptr) {
return kPointerSize == 8
? common->Int64Constant(reinterpret_cast<intptr_t>(ptr))
: common->Int32Constant(
static_cast<int32_t>(reinterpret_cast<intptr_t>(ptr)));
}
BasicBlockProfiler::Data* BasicBlockInstrumentor::Instrument(
CompilationInfo* info, Graph* graph, Schedule* schedule) {
// Skip the exit block in profiles, since the register allocator can't handle
// it and entry into it means falling off the end of the function anyway.
size_t n_blocks = static_cast<size_t>(schedule->RpoBlockCount()) - 1;
BasicBlockProfiler::Data* data =
info->isolate()->GetOrCreateBasicBlockProfiler()->NewData(n_blocks);
// Set the function name.
if (!info->shared_info().is_null() &&
info->shared_info()->name()->IsString()) {
OStringStream os;
String::cast(info->shared_info()->name())->PrintUC16(os);
data->SetFunctionName(&os);
}
// Capture the schedule string before instrumentation.
{
OStringStream os;
os << *schedule;
data->SetSchedule(&os);
}
// Add the increment instructions to the start of every block.
CommonOperatorBuilder common(graph->zone());
Node* zero = graph->NewNode(common.Int32Constant(0));
Node* one = graph->NewNode(common.Int32Constant(1));
MachineOperatorBuilder machine;
BasicBlockVector* blocks = schedule->rpo_order();
size_t block_number = 0;
for (BasicBlockVector::iterator it = blocks->begin(); block_number < n_blocks;
++it, ++block_number) {
BasicBlock* block = (*it);
data->SetBlockId(block_number, block->id());
// TODO(dcarney): wire effect and control deps for load and store.
// Construct increment operation.
Node* base = graph->NewNode(
PointerConstant(&common, data->GetCounterAddress(block_number)));
Node* load = graph->NewNode(machine.Load(kMachUint32), base, zero);
Node* inc = graph->NewNode(machine.Int32Add(), load, one);
Node* store = graph->NewNode(
machine.Store(StoreRepresentation(kMachUint32, kNoWriteBarrier)), base,
zero, inc);
// Insert the new nodes.
static const int kArraySize = 6;
Node* to_insert[kArraySize] = {zero, one, base, load, inc, store};
int insertion_start = block_number == 0 ? 0 : 2;
NodeVector* nodes = &block->nodes_;
NodeVector::iterator insertion_point = FindInsertionPoint(nodes);
nodes->insert(insertion_point, &to_insert[insertion_start],
&to_insert[kArraySize]);
// Tell the scheduler about the new nodes.
for (int i = insertion_start; i < kArraySize; ++i) {
schedule->SetBlockForNode(block, to_insert[i]);
}
}
return data;
}
} // namespace compiler
} // namespace internal
} // namespace v8
// 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_BASIC_BLOCK_INSTRUMENTOR_H_
#define V8_COMPILER_BASIC_BLOCK_INSTRUMENTOR_H_
#include "src/v8.h"
#include "src/basic-block-profiler.h"
namespace v8 {
namespace internal {
class CompilationInfo;
namespace compiler {
class Graph;
class Schedule;
class BasicBlockInstrumentor : public AllStatic {
public:
static BasicBlockProfiler::Data* Instrument(CompilationInfo* info,
Graph* graph, Schedule* schedule);
};
} // namespace compiler
} // namespace internal
} // namespace v8
#endif
......@@ -6,6 +6,7 @@
#include "src/base/platform/elapsed-timer.h"
#include "src/compiler/ast-graph-builder.h"
#include "src/compiler/basic-block-instrumentor.h"
#include "src/compiler/change-lowering.h"
#include "src/compiler/code-generator.h"
#include "src/compiler/graph-replay.h"
......@@ -315,6 +316,8 @@ Handle<Code> Pipeline::GenerateCode() {
VerifyAndPrintGraph(&graph, "Lowered generic");
}
source_positions.RemoveDecorator();
Handle<Code> code = Handle<Code>::null();
{
// Compute a schedule.
......@@ -381,6 +384,11 @@ Handle<Code> Pipeline::GenerateCode(Linkage* linkage, Graph* graph,
DCHECK_NOT_NULL(schedule);
CHECK(SupportedBackend());
BasicBlockProfiler::Data* profiler_data = NULL;
if (FLAG_turbo_profiling) {
profiler_data = BasicBlockInstrumentor::Instrument(info_, graph, schedule);
}
InstructionSequence sequence(linkage, graph, schedule);
// Select and schedule instructions covering the scheduled graph.
......@@ -417,7 +425,15 @@ Handle<Code> Pipeline::GenerateCode(Linkage* linkage, Graph* graph,
// Generate native sequence.
CodeGenerator generator(&sequence);
return generator.GenerateCode();
Handle<Code> code = generator.GenerateCode();
if (profiler_data != NULL) {
#if ENABLE_DISASSEMBLER
OStringStream os;
code->Disassemble(NULL, os);
profiler_data->SetCode(&os);
#endif
}
return code;
}
......
......@@ -22,6 +22,7 @@ namespace internal {
namespace compiler {
class BasicBlock;
class BasicBlockInstrumentor;
class Graph;
class ConstructScheduleData;
class CodeGenerator; // Because of a namespace bug in clang.
......@@ -279,6 +280,7 @@ class Schedule : public GenericGraph<BasicBlock> {
private:
friend class ScheduleVisualizer;
friend class BasicBlockInstrumentor;
void SetControlInput(BasicBlock* block, Node* node) {
block->control_input_ = node;
......
......@@ -46,6 +46,7 @@
#include "src/base/logging.h"
#include "src/base/platform/platform.h"
#include "src/base/sys-info.h"
#include "src/basic-block-profiler.h"
#include "src/d8-debug.h"
#include "src/debug.h"
#include "src/natives.h"
......@@ -1700,6 +1701,14 @@ int Shell::Main(int argc, char* argv[]) {
RunShell(isolate);
}
}
#ifndef V8_SHARED
// Dump basic block profiling data.
if (i::BasicBlockProfiler* profiler =
reinterpret_cast<i::Isolate*>(isolate)->basic_block_profiler()) {
i::OFStream os(stdout);
os << *profiler;
}
#endif // !V8_SHARED
isolate->Dispose();
V8::Dispose();
V8::ShutdownPlatform();
......
......@@ -345,6 +345,7 @@ DEFINE_BOOL(turbo_deoptimization, false, "enable deoptimization in TurboFan")
DEFINE_BOOL(turbo_inlining, false, "enable inlining in TurboFan")
DEFINE_BOOL(trace_turbo_inlining, false, "trace TurboFan inlining")
DEFINE_IMPLICATION(turbo_inlining, turbo_types)
DEFINE_BOOL(turbo_profiling, false, "enable profiling in TurboFan")
DEFINE_INT(typed_array_max_size_in_heap, 64,
"threshold for in-heap typed array")
......
......@@ -10,6 +10,7 @@
#include "src/base/platform/platform.h"
#include "src/base/sys-info.h"
#include "src/base/utils/random-number-generator.h"
#include "src/basic-block-profiler.h"
#include "src/bootstrapper.h"
#include "src/codegen.h"
#include "src/compilation-cache.h"
......@@ -1516,7 +1517,8 @@ Isolate::Isolate()
num_sweeper_threads_(0),
stress_deopt_count_(0),
next_optimization_id_(0),
use_counter_callback_(NULL) {
use_counter_callback_(NULL),
basic_block_profiler_(NULL) {
{
base::LockGuard<base::Mutex> lock_guard(thread_data_table_mutex_.Pointer());
CHECK(thread_data_table_);
......@@ -1640,6 +1642,10 @@ void Isolate::Deinit() {
delete runtime_profiler_;
runtime_profiler_ = NULL;
}
delete basic_block_profiler_;
basic_block_profiler_ = NULL;
heap_.TearDown();
logger_->TearDown();
......@@ -2363,6 +2369,14 @@ void Isolate::CountUsage(v8::Isolate::UseCounterFeature feature) {
}
BasicBlockProfiler* Isolate::GetOrCreateBasicBlockProfiler() {
if (basic_block_profiler_ == NULL) {
basic_block_profiler_ = new BasicBlockProfiler();
}
return basic_block_profiler_;
}
bool StackLimitCheck::JsHasOverflowed() const {
StackGuard* stack_guard = isolate_->stack_guard();
#ifdef USE_SIMULATOR
......
......@@ -32,6 +32,7 @@ class RandomNumberGenerator;
namespace internal {
class BasicBlockProfiler;
class Bootstrapper;
class CallInterfaceDescriptorData;
class CodeGenerator;
......@@ -1107,6 +1108,9 @@ class Isolate {
void SetUseCounterCallback(v8::Isolate::UseCounterCallback callback);
void CountUsage(v8::Isolate::UseCounterFeature feature);
BasicBlockProfiler* GetOrCreateBasicBlockProfiler();
BasicBlockProfiler* basic_block_profiler() { return basic_block_profiler_; }
static Isolate* NewForTesting() { return new Isolate(); }
private:
......@@ -1327,6 +1331,7 @@ class Isolate {
List<CallCompletedCallback> call_completed_callbacks_;
v8::Isolate::UseCounterCallback use_counter_callback_;
BasicBlockProfiler* basic_block_profiler_;
friend class ExecutionAccess;
friend class HandleScopeImplementer;
......
......@@ -53,6 +53,7 @@
'compiler/graph-tester.h',
'compiler/simplified-graph-builder.cc',
'compiler/simplified-graph-builder.h',
'compiler/test-basic-block-profiler.cc',
'compiler/test-branch-combine.cc',
'compiler/test-changes-lowering.cc',
'compiler/test-codegen-deopt.cc',
......
// 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/basic-block-profiler.h"
#include "test/cctest/cctest.h"
#include "test/cctest/compiler/codegen-tester.h"
#if V8_TURBOFAN_TARGET
using namespace v8::internal;
using namespace v8::internal::compiler;
typedef RawMachineAssembler::Label MLabel;
class BasicBlockProfilerTest : public RawMachineAssemblerTester<int32_t> {
public:
BasicBlockProfilerTest() : RawMachineAssemblerTester<int32_t>(kMachInt32) {
FLAG_turbo_profiling = true;
}
void ResetCounts() { isolate()->basic_block_profiler()->ResetCounts(); }
void Expect(size_t size, uint32_t* expected) {
CHECK_NE(NULL, isolate()->basic_block_profiler());
const BasicBlockProfiler::DataList* l =
isolate()->basic_block_profiler()->data_list();
CHECK_NE(0, static_cast<int>(l->size()));
const BasicBlockProfiler::Data* data = l->back();
CHECK_EQ(static_cast<int>(size), static_cast<int>(data->n_blocks()));
const uint32_t* counts = data->counts();
for (size_t i = 0; i < size; ++i) {
CHECK_EQ(static_cast<int>(expected[i]), static_cast<int>(counts[i]));
}
}
};
TEST(ProfileDiamond) {
BasicBlockProfilerTest m;
MLabel blocka, blockb, end;
m.Branch(m.Parameter(0), &blocka, &blockb);
m.Bind(&blocka);
m.Goto(&end);
m.Bind(&blockb);
m.Goto(&end);
m.Bind(&end);
m.Return(m.Int32Constant(0));
m.GenerateCode();
{
uint32_t expected[] = {0, 0, 0, 0};
m.Expect(arraysize(expected), expected);
}
m.Call(0);
{
uint32_t expected[] = {1, 1, 0, 1};
m.Expect(arraysize(expected), expected);
}
m.ResetCounts();
m.Call(1);
{
uint32_t expected[] = {1, 0, 1, 1};
m.Expect(arraysize(expected), expected);
}
m.Call(0);
{
uint32_t expected[] = {2, 1, 1, 2};
m.Expect(arraysize(expected), expected);
}
}
TEST(ProfileLoop) {
BasicBlockProfilerTest m;
MLabel header, body, end;
Node* one = m.Int32Constant(1);
m.Goto(&header);
m.Bind(&header);
Node* count = m.Phi(kMachInt32, m.Parameter(0), one);
m.Branch(count, &body, &end);
m.Bind(&body);
count->ReplaceInput(1, m.Int32Sub(count, one));
m.Goto(&header);
m.Bind(&end);
m.Return(one);
m.GenerateCode();
{
uint32_t expected[] = {0, 0, 0, 0};
m.Expect(arraysize(expected), expected);
}
uint32_t runs[] = {0, 1, 500, 10000};
for (size_t i = 0; i < arraysize(runs); i++) {
m.ResetCounts();
CHECK_EQ(1, m.Call(static_cast<int>(runs[i])));
uint32_t expected[] = {1, runs[i] + 1, runs[i], 1};
m.Expect(arraysize(expected), expected);
}
}
#endif // V8_TURBOFAN_TARGET
......@@ -351,6 +351,8 @@
'../../src/background-parsing-task.h',
'../../src/bailout-reason.cc',
'../../src/bailout-reason.h',
'../../src/basic-block-profiler.cc',
'../../src/basic-block-profiler.h',
'../../src/bignum-dtoa.cc',
'../../src/bignum-dtoa.h',
'../../src/bignum.cc',
......@@ -382,6 +384,8 @@
'../../src/compiler/access-builder.h',
'../../src/compiler/ast-graph-builder.cc',
'../../src/compiler/ast-graph-builder.h',
'../../src/compiler/basic-block-instrumentor.cc',
'../../src/compiler/basic-block-instrumentor.h',
'../../src/compiler/change-lowering.cc',
'../../src/compiler/change-lowering.h',
'../../src/compiler/code-generator-impl.h',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment