blob: 31d53e44ee327680c41de747d84fadfc4f65e150 [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 "src/base/platform/elapsed-timer.h"
#include "src/compiler/ast-graph-builder.h"
#include "src/compiler/change-lowering.h"
#include "src/compiler/code-generator.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/phi-reducer.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/hydrogen.h"
#include "src/ostreams.h"
#include "src/utils.h"
namespace v8 {
namespace internal {
namespace compiler {
class PhaseStats {
public:
enum PhaseKind { CREATE_GRAPH, OPTIMIZATION, CODEGEN };
PhaseStats(CompilationInfo* info, PhaseKind kind, const char* name)
: info_(info),
kind_(kind),
name_(name),
size_(info->zone()->allocation_size()) {
if (FLAG_turbo_stats) {
timer_.Start();
}
}
~PhaseStats() {
if (FLAG_turbo_stats) {
base::TimeDelta delta = timer_.Elapsed();
size_t bytes = info_->zone()->allocation_size() - size_;
HStatistics* stats = info_->isolate()->GetTStatistics();
stats->SaveTiming(name_, delta, static_cast<int>(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_;
PhaseKind kind_;
const char* name_;
size_t size_;
base::ElapsedTimer timer_;
};
static inline bool VerifyGraphs() {
#ifdef DEBUG
return true;
#else
return FLAG_turbo_verify;
#endif
}
void Pipeline::VerifyAndPrintGraph(Graph* graph, const char* phase) {
if (FLAG_trace_turbo) {
char buffer[256];
Vector<char> filename(buffer, sizeof(buffer));
if (!info_->shared_info().is_null()) {
SmartArrayPointer<char> functionname =
info_->shared_info()->DebugName()->ToCString();
if (strlen(functionname.get()) > 0) {
SNPrintF(filename, "turbo-%s-%s.dot", functionname.get(), phase);
} else {
SNPrintF(filename, "turbo-%p-%s.dot", static_cast<void*>(info_), phase);
}
} else {
SNPrintF(filename, "turbo-none-%s.dot", phase);
}
std::replace(filename.start(), filename.start() + filename.length(), ' ',
'_');
FILE* file = base::OS::FOpen(filename.start(), "w+");
OFStream of(file);
of << AsDOT(*graph);
fclose(file);
OFStream os(stdout);
os << "-- " << phase << " graph printed to file " << filename.start()
<< "\n";
}
if (VerifyGraphs()) Verifier::Run(graph);
}
class AstGraphBuilderWithPositions : public AstGraphBuilder {
public:
explicit AstGraphBuilderWithPositions(CompilationInfo* info, JSGraph* jsgraph,
SourcePositionTable* source_positions)
: AstGraphBuilder(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;
}
Handle<Code> Pipeline::GenerateCode() {
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 OSR work and remove this bailout.
info()->is_osr()) {
return Handle<Code>::null();
}
if (FLAG_turbo_stats) isolate()->GetTStatistics()->Initialize(info_);
if (FLAG_trace_turbo) {
OFStream os(stdout);
os << "---------------------------------------------------\n"
<< "Begin compiling method "
<< info()->function()->debug_name()->ToCString().get()
<< " using Turbofan" << endl;
}
// Build the graph.
Graph graph(zone());
SourcePositionTable source_positions(&graph);
source_positions.AddDecorator();
// TODO(turbofan): there is no need to type anything during initial graph
// construction. This is currently only needed for the node cache, which the
// typer could sweep over later.
Typer typer(zone());
MachineOperatorBuilder machine;
CommonOperatorBuilder common(zone());
JSOperatorBuilder javascript(zone());
JSGraph jsgraph(&graph, &common, &javascript, &typer, &machine);
Node* context_node;
{
PhaseStats graph_builder_stats(info(), PhaseStats::CREATE_GRAPH,
"graph builder");
AstGraphBuilderWithPositions graph_builder(info(), &jsgraph,
&source_positions);
graph_builder.CreateGraph();
context_node = graph_builder.GetFunctionContext();
}
{
PhaseStats phi_reducer_stats(info(), PhaseStats::CREATE_GRAPH,
"phi reduction");
PhiReducer phi_reducer;
GraphReducer graph_reducer(&graph);
graph_reducer.AddReducer(&phi_reducer);
graph_reducer.ReduceGraph();
// TODO(mstarzinger): Running reducer once ought to be enough for everyone.
graph_reducer.ReduceGraph();
graph_reducer.ReduceGraph();
}
VerifyAndPrintGraph(&graph, "Initial untyped");
if (info()->is_context_specializing()) {
SourcePositionTable::Scope pos(&source_positions,
SourcePosition::Unknown());
// Specialize the code to the context as aggressively as possible.
JSContextSpecializer spec(info(), &jsgraph, context_node);
spec.SpecializeToContext();
VerifyAndPrintGraph(&graph, "Context specialized");
}
if (info()->is_inlining_enabled()) {
SourcePositionTable::Scope pos(&source_positions,
SourcePosition::Unknown());
JSInliner inliner(info(), &jsgraph);
inliner.Inline();
VerifyAndPrintGraph(&graph, "Inlined");
}
// Print a replay of the initial graph.
if (FLAG_print_turbo_replay) {
GraphReplayPrinter::PrintReplay(&graph);
}
if (info()->is_typing_enabled()) {
{
// Type the graph.
PhaseStats typer_stats(info(), PhaseStats::CREATE_GRAPH, "typer");
typer.Run(&graph, info()->context());
VerifyAndPrintGraph(&graph, "Typed");
}
// All new nodes must be typed.
typer.DecorateGraph(&graph);
{
// Lower JSOperators where we can determine types.
PhaseStats lowering_stats(info(), PhaseStats::CREATE_GRAPH,
"typed lowering");
SourcePositionTable::Scope pos(&source_positions,
SourcePosition::Unknown());
JSTypedLowering lowering(&jsgraph);
GraphReducer graph_reducer(&graph);
graph_reducer.AddReducer(&lowering);
graph_reducer.ReduceGraph();
VerifyAndPrintGraph(&graph, "Lowered typed");
}
{
// Lower simplified operators and insert changes.
PhaseStats lowering_stats(info(), PhaseStats::CREATE_GRAPH,
"simplified lowering");
SourcePositionTable::Scope pos(&source_positions,
SourcePosition::Unknown());
SimplifiedLowering lowering(&jsgraph);
lowering.LowerAllNodes();
VerifyAndPrintGraph(&graph, "Lowered simplified");
}
{
// Lower changes that have been inserted before.
PhaseStats lowering_stats(info(), PhaseStats::OPTIMIZATION,
"change lowering");
SourcePositionTable::Scope pos(&source_positions,
SourcePosition::Unknown());
Linkage linkage(info());
// TODO(turbofan): Value numbering disabled for now.
// ValueNumberingReducer vn_reducer(zone());
SimplifiedOperatorReducer simple_reducer(&jsgraph);
ChangeLowering lowering(&jsgraph, &linkage);
MachineOperatorReducer mach_reducer(&jsgraph);
GraphReducer graph_reducer(&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();
VerifyAndPrintGraph(&graph, "Lowered changes");
}
}
Handle<Code> code = Handle<Code>::null();
if (SupportedTarget()) {
{
// Lower any remaining generic JSOperators.
PhaseStats lowering_stats(info(), PhaseStats::CREATE_GRAPH,
"generic lowering");
SourcePositionTable::Scope pos(&source_positions,
SourcePosition::Unknown());
JSGenericLowering lowering(info(), &jsgraph);
GraphReducer graph_reducer(&graph);
graph_reducer.AddReducer(&lowering);
graph_reducer.ReduceGraph();
VerifyAndPrintGraph(&graph, "Lowered generic");
}
{
// Compute a schedule.
Schedule* schedule = ComputeSchedule(&graph);
// Generate optimized code.
PhaseStats codegen_stats(info(), PhaseStats::CODEGEN, "codegen");
Linkage linkage(info());
code = GenerateCode(&linkage, &graph, schedule, &source_positions);
info()->SetCode(code);
}
// Print optimized code.
v8::internal::CodeGenerator::PrintCode(code, info());
}
if (FLAG_trace_turbo) {
OFStream os(stdout);
os << "--------------------------------------------------\n"
<< "Finished compiling method "
<< info()->function()->debug_name()->ToCString().get()
<< " using Turbofan" << endl;
}
return code;
}
Schedule* Pipeline::ComputeSchedule(Graph* graph) {
PhaseStats schedule_stats(info(), PhaseStats::CODEGEN, "scheduling");
Schedule* schedule = Scheduler::ComputeSchedule(graph);
TraceSchedule(schedule);
if (VerifyGraphs()) ScheduleVerifier::Run(schedule);
return schedule;
}
Handle<Code> Pipeline::GenerateCodeForMachineGraph(Linkage* linkage,
Graph* graph,
Schedule* schedule) {
CHECK(SupportedBackend());
if (schedule == NULL) {
VerifyAndPrintGraph(graph, "Machine");
schedule = ComputeSchedule(graph);
}
TraceSchedule(schedule);
SourcePositionTable source_positions(graph);
Handle<Code> code = GenerateCode(linkage, graph, schedule, &source_positions);
#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, Graph* graph,
Schedule* schedule,
SourcePositionTable* source_positions) {
DCHECK_NOT_NULL(graph);
DCHECK_NOT_NULL(linkage);
DCHECK_NOT_NULL(schedule);
CHECK(SupportedBackend());
InstructionSequence sequence(linkage, graph, schedule);
// Select and schedule instructions covering the scheduled graph.
{
InstructionSelector selector(&sequence, source_positions);
selector.SelectInstructions();
}
if (FLAG_trace_turbo) {
OFStream os(stdout);
os << "----- Instruction sequence before register allocation -----\n"
<< sequence;
}
// Allocate registers.
{
int node_count = graph->NodeCount();
if (node_count > UnallocatedOperand::kMaxVirtualRegisters) {
linkage->info()->set_bailout_reason(kNotEnoughVirtualRegistersForValues);
return Handle<Code>::null();
}
RegisterAllocator allocator(&sequence);
if (!allocator.Allocate()) {
linkage->info()->set_bailout_reason(kNotEnoughVirtualRegistersRegalloc);
return Handle<Code>::null();
}
}
if (FLAG_trace_turbo) {
OFStream os(stdout);
os << "----- Instruction sequence after register allocation -----\n"
<< sequence;
}
// Generate native sequence.
CodeGenerator generator(&sequence);
return generator.GenerateCode();
}
void Pipeline::SetUp() {
InstructionOperand::SetUpCaches();
}
void Pipeline::TearDown() {
InstructionOperand::TearDownCaches();
}
} // namespace compiler
} // namespace internal
} // namespace v8