blob: 0dcb4086c896c07de9ca67512e407231afcae272 [file] [log] [blame]
// 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/pipeline.h"
#include <fstream> // NOLINT(readability/streams)
#include <sstream>
#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/control-reducer.h"
#include "src/compiler/graph-replay.h"
#include "src/compiler/graph-visualizer.h"
#include "src/compiler/instruction.h"
#include "src/compiler/instruction-selector.h"
#include "src/compiler/js-context-specialization.h"
#include "src/compiler/js-generic-lowering.h"
#include "src/compiler/js-inlining.h"
#include "src/compiler/js-typed-lowering.h"
#include "src/compiler/machine-operator-reducer.h"
#include "src/compiler/pipeline-statistics.h"
#include "src/compiler/register-allocator.h"
#include "src/compiler/schedule.h"
#include "src/compiler/scheduler.h"
#include "src/compiler/simplified-lowering.h"
#include "src/compiler/simplified-operator-reducer.h"
#include "src/compiler/typer.h"
#include "src/compiler/value-numbering-reducer.h"
#include "src/compiler/verifier.h"
#include "src/compiler/zone-pool.h"
#include "src/ostreams.h"
#include "src/utils.h"
namespace v8 {
namespace internal {
namespace compiler {
class PipelineData {
public:
explicit PipelineData(CompilationInfo* info, ZonePool* zone_pool,
PipelineStatistics* pipeline_statistics)
: isolate_(info->zone()->isolate()),
outer_zone_(info->zone()),
zone_pool_(zone_pool),
pipeline_statistics_(pipeline_statistics),
graph_zone_scope_(zone_pool_),
graph_zone_(graph_zone_scope_.zone()),
graph_(new (graph_zone()) Graph(graph_zone())),
source_positions_(new SourcePositionTable(graph())),
machine_(new (graph_zone()) MachineOperatorBuilder(
kMachPtr, InstructionSelector::SupportedMachineOperatorFlags())),
common_(new (graph_zone()) CommonOperatorBuilder(graph_zone())),
javascript_(new (graph_zone()) JSOperatorBuilder(graph_zone())),
jsgraph_(new (graph_zone())
JSGraph(graph(), common(), javascript(), machine())),
typer_(new Typer(graph(), info->context())),
schedule_(NULL),
instruction_zone_scope_(zone_pool_),
instruction_zone_(instruction_zone_scope_.zone()) {}
// For machine graph testing only.
PipelineData(Graph* graph, Schedule* schedule, ZonePool* zone_pool)
: isolate_(graph->zone()->isolate()),
outer_zone_(NULL),
zone_pool_(zone_pool),
pipeline_statistics_(NULL),
graph_zone_scope_(zone_pool_),
graph_zone_(NULL),
graph_(graph),
source_positions_(new SourcePositionTable(graph)),
machine_(NULL),
common_(NULL),
javascript_(NULL),
jsgraph_(NULL),
typer_(NULL),
schedule_(schedule),
instruction_zone_scope_(zone_pool_),
instruction_zone_(instruction_zone_scope_.zone()) {}
~PipelineData() {
DeleteInstructionZone();
DeleteGraphZone();
}
Isolate* isolate() const { return isolate_; }
ZonePool* zone_pool() const { return zone_pool_; }
PipelineStatistics* pipeline_statistics() { return pipeline_statistics_; }
Zone* graph_zone() const { return graph_zone_; }
Graph* graph() const { return graph_; }
SourcePositionTable* source_positions() const {
return source_positions_.get();
}
MachineOperatorBuilder* machine() const { return machine_; }
CommonOperatorBuilder* common() const { return common_; }
JSOperatorBuilder* javascript() const { return javascript_; }
JSGraph* jsgraph() const { return jsgraph_; }
Typer* typer() const { return typer_.get(); }
Schedule* schedule() const { return schedule_; }
void set_schedule(Schedule* schedule) {
DCHECK_EQ(NULL, schedule_);
schedule_ = schedule;
}
Zone* instruction_zone() const { return instruction_zone_; }
void DeleteGraphZone() {
// Destroy objects with destructors first.
source_positions_.Reset(NULL);
typer_.Reset(NULL);
if (graph_zone_ == NULL) return;
// Destroy zone and clear pointers.
graph_zone_scope_.Destroy();
graph_zone_ = NULL;
graph_ = NULL;
machine_ = NULL;
common_ = NULL;
javascript_ = NULL;
jsgraph_ = NULL;
schedule_ = NULL;
}
void DeleteInstructionZone() {
if (instruction_zone_ == NULL) return;
instruction_zone_scope_.Destroy();
instruction_zone_ = NULL;
}
private:
Isolate* isolate_;
Zone* outer_zone_;
ZonePool* zone_pool_;
PipelineStatistics* pipeline_statistics_;
ZonePool::Scope graph_zone_scope_;
Zone* graph_zone_;
// All objects in the following group of fields are allocated in graph_zone_.
// They are all set to NULL when the graph_zone_ is destroyed.
Graph* graph_;
// TODO(dcarney): make this into a ZoneObject.
SmartPointer<SourcePositionTable> source_positions_;
MachineOperatorBuilder* machine_;
CommonOperatorBuilder* common_;
JSOperatorBuilder* javascript_;
JSGraph* jsgraph_;
// TODO(dcarney): make this into a ZoneObject.
SmartPointer<Typer> typer_;
Schedule* schedule_;
// All objects in the following group of fields are allocated in
// instruction_zone_. They are all set to NULL when the instruction_zone_ is
// destroyed.
ZonePool::Scope instruction_zone_scope_;
Zone* instruction_zone_;
DISALLOW_COPY_AND_ASSIGN(PipelineData);
};
static inline bool VerifyGraphs() {
#ifdef DEBUG
return true;
#else
return FLAG_turbo_verify;
#endif
}
struct TurboCfgFile : public std::ofstream {
explicit TurboCfgFile(Isolate* isolate)
: std::ofstream(isolate->GetTurboCfgFileName().c_str(),
std::ios_base::app) {}
};
void Pipeline::VerifyAndPrintGraph(
Graph* graph, const char* phase, bool untyped) {
if (FLAG_trace_turbo) {
char buffer[256];
Vector<char> filename(buffer, sizeof(buffer));
SmartArrayPointer<char> functionname;
if (!info_->shared_info().is_null()) {
functionname = info_->shared_info()->DebugName()->ToCString();
if (strlen(functionname.get()) > 0) {
SNPrintF(filename, "turbo-%s-%s", functionname.get(), phase);
} else {
SNPrintF(filename, "turbo-%p-%s", static_cast<void*>(info_), phase);
}
} else {
SNPrintF(filename, "turbo-none-%s", phase);
}
std::replace(filename.start(), filename.start() + filename.length(), ' ',
'_');
char dot_buffer[256];
Vector<char> dot_filename(dot_buffer, sizeof(dot_buffer));
SNPrintF(dot_filename, "%s.dot", filename.start());
FILE* dot_file = base::OS::FOpen(dot_filename.start(), "w+");
OFStream dot_of(dot_file);
dot_of << AsDOT(*graph);
fclose(dot_file);
char json_buffer[256];
Vector<char> json_filename(json_buffer, sizeof(json_buffer));
SNPrintF(json_filename, "%s.json", filename.start());
FILE* json_file = base::OS::FOpen(json_filename.start(), "w+");
OFStream json_of(json_file);
json_of << AsJSON(*graph);
fclose(json_file);
OFStream os(stdout);
os << "-- " << phase << " graph printed to file " << filename.start()
<< "\n";
}
if (VerifyGraphs()) {
Verifier::Run(graph,
FLAG_turbo_types && !untyped ? Verifier::TYPED : Verifier::UNTYPED);
}
}
class AstGraphBuilderWithPositions : public AstGraphBuilder {
public:
explicit AstGraphBuilderWithPositions(Zone* local_zone, CompilationInfo* info,
JSGraph* jsgraph,
SourcePositionTable* source_positions)
: AstGraphBuilder(local_zone, info, jsgraph),
source_positions_(source_positions) {}
bool CreateGraph() {
SourcePositionTable::Scope pos(source_positions_,
SourcePosition::Unknown());
return AstGraphBuilder::CreateGraph();
}
#define DEF_VISIT(type) \
virtual void Visit##type(type* node) OVERRIDE { \
SourcePositionTable::Scope pos(source_positions_, \
SourcePosition(node->position())); \
AstGraphBuilder::Visit##type(node); \
}
AST_NODE_LIST(DEF_VISIT)
#undef DEF_VISIT
private:
SourcePositionTable* source_positions_;
};
static void TraceSchedule(Schedule* schedule) {
if (!FLAG_trace_turbo) return;
OFStream os(stdout);
os << "-- Schedule --------------------------------------\n" << *schedule;
}
static SmartArrayPointer<char> GetDebugName(CompilationInfo* info) {
SmartArrayPointer<char> name;
if (info->IsStub()) {
if (info->code_stub() != NULL) {
CodeStub::Major major_key = info->code_stub()->MajorKey();
const char* major_name = CodeStub::MajorName(major_key, false);
size_t len = strlen(major_name);
name.Reset(new char[len]);
memcpy(name.get(), major_name, len);
}
} else {
AllowHandleDereference allow_deref;
name = info->function()->debug_name()->ToCString();
}
return name;
}
Handle<Code> Pipeline::GenerateCode() {
// This list must be kept in sync with DONT_TURBOFAN_NODE in ast.cc.
if (info()->function()->dont_optimize_reason() == kTryCatchStatement ||
info()->function()->dont_optimize_reason() == kTryFinallyStatement ||
// TODO(turbofan): Make ES6 for-of work and remove this bailout.
info()->function()->dont_optimize_reason() == kForOfStatement ||
// TODO(turbofan): Make super work and remove this bailout.
info()->function()->dont_optimize_reason() == kSuperReference ||
// TODO(turbofan): Make class literals work and remove this bailout.
info()->function()->dont_optimize_reason() == kClassLiteral ||
// TODO(turbofan): Make OSR work and remove this bailout.
info()->is_osr()) {
return Handle<Code>::null();
}
ZonePool zone_pool(isolate());
SmartPointer<PipelineStatistics> pipeline_statistics;
if (FLAG_turbo_stats) {
pipeline_statistics.Reset(new PipelineStatistics(info(), &zone_pool));
pipeline_statistics->BeginPhaseKind("graph creation");
}
if (FLAG_trace_turbo) {
OFStream os(stdout);
os << "---------------------------------------------------\n"
<< "Begin compiling method " << GetDebugName(info()).get()
<< " using Turbofan" << std::endl;
TurboCfgFile tcf(isolate());
tcf << AsC1VCompilation(info());
}
// Initialize the graph and builders.
PipelineData data(info(), &zone_pool, pipeline_statistics.get());
data.source_positions()->AddDecorator();
Node* context_node;
{
PhaseScope phase_scope(pipeline_statistics.get(), "graph builder");
ZonePool::Scope zone_scope(data.zone_pool());
AstGraphBuilderWithPositions graph_builder(
zone_scope.zone(), info(), data.jsgraph(), data.source_positions());
graph_builder.CreateGraph();
context_node = graph_builder.GetFunctionContext();
}
VerifyAndPrintGraph(data.graph(), "Initial untyped", true);
{
PhaseScope phase_scope(pipeline_statistics.get(),
"early control reduction");
SourcePositionTable::Scope pos(data.source_positions(),
SourcePosition::Unknown());
ZonePool::Scope zone_scope(data.zone_pool());
ControlReducer::ReduceGraph(zone_scope.zone(), data.jsgraph(),
data.common());
VerifyAndPrintGraph(data.graph(), "Early Control reduced", true);
}
if (info()->is_context_specializing()) {
SourcePositionTable::Scope pos(data.source_positions(),
SourcePosition::Unknown());
// Specialize the code to the context as aggressively as possible.
JSContextSpecializer spec(info(), data.jsgraph(), context_node);
spec.SpecializeToContext();
VerifyAndPrintGraph(data.graph(), "Context specialized", true);
}
if (info()->is_inlining_enabled()) {
PhaseScope phase_scope(pipeline_statistics.get(), "inlining");
SourcePositionTable::Scope pos(data.source_positions(),
SourcePosition::Unknown());
ZonePool::Scope zone_scope(data.zone_pool());
JSInliner inliner(zone_scope.zone(), info(), data.jsgraph());
inliner.Inline();
VerifyAndPrintGraph(data.graph(), "Inlined", true);
}
// Print a replay of the initial graph.
if (FLAG_print_turbo_replay) {
GraphReplayPrinter::PrintReplay(data.graph());
}
// Bailout here in case target architecture is not supported.
if (!SupportedTarget()) return Handle<Code>::null();
if (info()->is_typing_enabled()) {
{
// Type the graph.
PhaseScope phase_scope(pipeline_statistics.get(), "typer");
data.typer()->Run();
VerifyAndPrintGraph(data.graph(), "Typed");
}
}
if (!pipeline_statistics.is_empty()) {
pipeline_statistics->BeginPhaseKind("lowering");
}
if (info()->is_typing_enabled()) {
{
// Lower JSOperators where we can determine types.
PhaseScope phase_scope(pipeline_statistics.get(), "typed lowering");
SourcePositionTable::Scope pos(data.source_positions(),
SourcePosition::Unknown());
ValueNumberingReducer vn_reducer(data.graph_zone());
JSTypedLowering lowering(data.jsgraph());
SimplifiedOperatorReducer simple_reducer(data.jsgraph());
GraphReducer graph_reducer(data.graph());
graph_reducer.AddReducer(&vn_reducer);
graph_reducer.AddReducer(&lowering);
graph_reducer.AddReducer(&simple_reducer);
graph_reducer.ReduceGraph();
VerifyAndPrintGraph(data.graph(), "Lowered typed");
}
{
// Lower simplified operators and insert changes.
PhaseScope phase_scope(pipeline_statistics.get(), "simplified lowering");
SourcePositionTable::Scope pos(data.source_positions(),
SourcePosition::Unknown());
SimplifiedLowering lowering(data.jsgraph());
lowering.LowerAllNodes();
ValueNumberingReducer vn_reducer(data.graph_zone());
SimplifiedOperatorReducer simple_reducer(data.jsgraph());
GraphReducer graph_reducer(data.graph());
graph_reducer.AddReducer(&vn_reducer);
graph_reducer.AddReducer(&simple_reducer);
graph_reducer.ReduceGraph();
VerifyAndPrintGraph(data.graph(), "Lowered simplified");
}
{
// Lower changes that have been inserted before.
PhaseScope phase_scope(pipeline_statistics.get(), "change lowering");
SourcePositionTable::Scope pos(data.source_positions(),
SourcePosition::Unknown());
Linkage linkage(data.graph_zone(), info());
ValueNumberingReducer vn_reducer(data.graph_zone());
SimplifiedOperatorReducer simple_reducer(data.jsgraph());
ChangeLowering lowering(data.jsgraph(), &linkage);
MachineOperatorReducer mach_reducer(data.jsgraph());
GraphReducer graph_reducer(data.graph());
// TODO(titzer): Figure out if we should run all reducers at once here.
graph_reducer.AddReducer(&vn_reducer);
graph_reducer.AddReducer(&simple_reducer);
graph_reducer.AddReducer(&lowering);
graph_reducer.AddReducer(&mach_reducer);
graph_reducer.ReduceGraph();
// TODO(jarin, rossberg): Remove UNTYPED once machine typing works.
VerifyAndPrintGraph(data.graph(), "Lowered changes", true);
}
{
PhaseScope phase_scope(pipeline_statistics.get(),
"late control reduction");
SourcePositionTable::Scope pos(data.source_positions(),
SourcePosition::Unknown());
ZonePool::Scope zone_scope(data.zone_pool());
ControlReducer::ReduceGraph(zone_scope.zone(), data.jsgraph(),
data.common());
VerifyAndPrintGraph(data.graph(), "Late Control reduced");
}
}
{
// Lower any remaining generic JSOperators.
PhaseScope phase_scope(pipeline_statistics.get(), "generic lowering");
SourcePositionTable::Scope pos(data.source_positions(),
SourcePosition::Unknown());
JSGenericLowering lowering(info(), data.jsgraph());
GraphReducer graph_reducer(data.graph());
graph_reducer.AddReducer(&lowering);
graph_reducer.ReduceGraph();
// TODO(jarin, rossberg): Remove UNTYPED once machine typing works.
VerifyAndPrintGraph(data.graph(), "Lowered generic", true);
}
if (!pipeline_statistics.is_empty()) {
pipeline_statistics->BeginPhaseKind("block building");
}
data.source_positions()->RemoveDecorator();
// Compute a schedule.
ComputeSchedule(&data);
Handle<Code> code = Handle<Code>::null();
{
// Generate optimized code.
Linkage linkage(data.instruction_zone(), info());
code = GenerateCode(&linkage, &data);
info()->SetCode(code);
}
// Print optimized code.
v8::internal::CodeGenerator::PrintCode(code, info());
if (FLAG_trace_turbo) {
OFStream os(stdout);
os << "--------------------------------------------------\n"
<< "Finished compiling method " << GetDebugName(info()).get()
<< " using Turbofan" << std::endl;
}
return code;
}
void Pipeline::ComputeSchedule(PipelineData* data) {
PhaseScope phase_scope(data->pipeline_statistics(), "scheduling");
Schedule* schedule =
Scheduler::ComputeSchedule(data->zone_pool(), data->graph());
TraceSchedule(schedule);
if (VerifyGraphs()) ScheduleVerifier::Run(schedule);
data->set_schedule(schedule);
}
Handle<Code> Pipeline::GenerateCodeForMachineGraph(Linkage* linkage,
Graph* graph,
Schedule* schedule) {
ZonePool zone_pool(isolate());
CHECK(SupportedBackend());
PipelineData data(graph, schedule, &zone_pool);
if (schedule == NULL) {
// TODO(rossberg): Should this really be untyped?
VerifyAndPrintGraph(graph, "Machine", true);
ComputeSchedule(&data);
} else {
TraceSchedule(schedule);
}
Handle<Code> code = GenerateCode(linkage, &data);
#if ENABLE_DISASSEMBLER
if (!code.is_null() && FLAG_print_opt_code) {
CodeTracer::Scope tracing_scope(isolate()->GetCodeTracer());
OFStream os(tracing_scope.file());
code->Disassemble("test code", os);
}
#endif
return code;
}
Handle<Code> Pipeline::GenerateCode(Linkage* linkage, PipelineData* data) {
DCHECK_NOT_NULL(linkage);
DCHECK_NOT_NULL(data->graph());
DCHECK_NOT_NULL(data->schedule());
CHECK(SupportedBackend());
BasicBlockProfiler::Data* profiler_data = NULL;
if (FLAG_turbo_profiling) {
profiler_data = BasicBlockInstrumentor::Instrument(info(), data->graph(),
data->schedule());
}
InstructionSequence sequence(data->instruction_zone(), data->schedule());
// Select and schedule instructions covering the scheduled graph.
{
PhaseScope phase_scope(data->pipeline_statistics(), "select instructions");
ZonePool::Scope zone_scope(data->zone_pool());
InstructionSelector selector(zone_scope.zone(), data->graph(), linkage,
&sequence, data->schedule(),
data->source_positions());
selector.SelectInstructions();
}
if (FLAG_trace_turbo) {
OFStream os(stdout);
os << "----- Instruction sequence before register allocation -----\n"
<< sequence;
TurboCfgFile tcf(isolate());
tcf << AsC1V("CodeGen", data->schedule(), data->source_positions(),
&sequence);
}
data->DeleteGraphZone();
if (data->pipeline_statistics() != NULL) {
data->pipeline_statistics()->BeginPhaseKind("register allocation");
}
// Allocate registers.
Frame frame;
{
int node_count = sequence.VirtualRegisterCount();
if (node_count > UnallocatedOperand::kMaxVirtualRegisters) {
info()->AbortOptimization(kNotEnoughVirtualRegistersForValues);
return Handle<Code>::null();
}
ZonePool::Scope zone_scope(data->zone_pool());
SmartArrayPointer<char> debug_name;
#ifdef DEBUG
debug_name = GetDebugName(info());
#endif
RegisterAllocator allocator(RegisterAllocator::PlatformConfig(),
zone_scope.zone(), &frame, &sequence,
debug_name.get());
if (!allocator.Allocate(data->pipeline_statistics())) {
info()->AbortOptimization(kNotEnoughVirtualRegistersRegalloc);
return Handle<Code>::null();
}
if (FLAG_trace_turbo) {
TurboCfgFile tcf(isolate());
tcf << AsC1VAllocator("CodeGen", &allocator);
}
}
if (FLAG_trace_turbo) {
OFStream os(stdout);
os << "----- Instruction sequence after register allocation -----\n"
<< sequence;
}
if (data->pipeline_statistics() != NULL) {
data->pipeline_statistics()->BeginPhaseKind("code generation");
}
// Generate native sequence.
Handle<Code> code;
{
PhaseScope phase_scope(data->pipeline_statistics(), "generate code");
CodeGenerator generator(&frame, linkage, &sequence, info());
code = generator.GenerateCode();
}
if (profiler_data != NULL) {
#if ENABLE_DISASSEMBLER
std::ostringstream os;
code->Disassemble(NULL, os);
profiler_data->SetCode(&os);
#endif
}
return code;
}
void Pipeline::SetUp() {
InstructionOperand::SetUpCaches();
}
void Pipeline::TearDown() {
InstructionOperand::TearDownCaches();
}
} // namespace compiler
} // namespace internal
} // namespace v8