blob: a46686b65e7ebf0e4ed87b7e77c7b57d8f4e65f1 [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/phi-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 {
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;
}
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("create graph");
}
if (FLAG_trace_turbo) {
OFStream os(stdout);
os << "---------------------------------------------------\n"
<< "Begin compiling method "
<< info()->function()->debug_name()->ToCString().get()
<< " using Turbofan" << std::endl;
TurboCfgFile tcf(isolate());
tcf << AsC1VCompilation(info());
}
// 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(&graph, info()->context());
MachineOperatorBuilder machine(
kMachPtr, InstructionSelector::SupportedMachineOperatorFlags());
CommonOperatorBuilder common(zone());
JSOperatorBuilder javascript(zone());
JSGraph jsgraph(&graph, &common, &javascript, &machine);
Node* context_node;
{
PhaseScope phase_scope(pipeline_statistics.get(), "graph builder");
ZonePool::Scope zone_scope(&zone_pool);
AstGraphBuilderWithPositions graph_builder(zone_scope.zone(), info(),
&jsgraph, &source_positions);
graph_builder.CreateGraph();
context_node = graph_builder.GetFunctionContext();
}
{
PhaseScope phase_scope(pipeline_statistics.get(), "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", true);
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", true);
}
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);
JSInliner inliner(zone_scope.zone(), info(), &jsgraph);
inliner.Inline();
VerifyAndPrintGraph(&graph, "Inlined", true);
}
// Print a replay of the initial graph.
if (FLAG_print_turbo_replay) {
GraphReplayPrinter::PrintReplay(&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");
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.
PhaseScope phase_scope(pipeline_statistics.get(), "typed lowering");
SourcePositionTable::Scope pos(&source_positions,
SourcePosition::Unknown());
ValueNumberingReducer vn_reducer(zone());
JSTypedLowering lowering(&jsgraph);
SimplifiedOperatorReducer simple_reducer(&jsgraph);
GraphReducer graph_reducer(&graph);
graph_reducer.AddReducer(&vn_reducer);
graph_reducer.AddReducer(&lowering);
graph_reducer.AddReducer(&simple_reducer);
graph_reducer.ReduceGraph();
VerifyAndPrintGraph(&graph, "Lowered typed");
}
{
// Lower simplified operators and insert changes.
PhaseScope phase_scope(pipeline_statistics.get(), "simplified lowering");
SourcePositionTable::Scope pos(&source_positions,
SourcePosition::Unknown());
SimplifiedLowering lowering(&jsgraph);
lowering.LowerAllNodes();
ValueNumberingReducer vn_reducer(zone());
SimplifiedOperatorReducer simple_reducer(&jsgraph);
GraphReducer graph_reducer(&graph);
graph_reducer.AddReducer(&vn_reducer);
graph_reducer.AddReducer(&simple_reducer);
graph_reducer.ReduceGraph();
VerifyAndPrintGraph(&graph, "Lowered simplified");
}
{
// Lower changes that have been inserted before.
PhaseScope phase_scope(pipeline_statistics.get(), "change lowering");
SourcePositionTable::Scope pos(&source_positions,
SourcePosition::Unknown());
Linkage linkage(info());
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();
// TODO(jarin, rossberg): Remove UNTYPED once machine typing works.
VerifyAndPrintGraph(&graph, "Lowered changes", true);
}
{
PhaseScope phase_scope(pipeline_statistics.get(), "control reduction");
SourcePositionTable::Scope pos(&source_positions,
SourcePosition::Unknown());
ZonePool::Scope zone_scope(&zone_pool);
ControlReducer::ReduceGraph(zone_scope.zone(), &jsgraph, &common);
VerifyAndPrintGraph(&graph, "Control reduced");
}
}
{
// Lower any remaining generic JSOperators.
PhaseScope phase_scope(pipeline_statistics.get(), "generic lowering");
SourcePositionTable::Scope pos(&source_positions,
SourcePosition::Unknown());
JSGenericLowering lowering(info(), &jsgraph);
GraphReducer graph_reducer(&graph);
graph_reducer.AddReducer(&lowering);
graph_reducer.ReduceGraph();
// TODO(jarin, rossberg): Remove UNTYPED once machine typing works.
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> code = Handle<Code>::null();
{
// Generate optimized code.
Linkage linkage(info());
code = GenerateCode(pipeline_statistics.get(), &zone_pool, &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" << std::endl;
}
return code;
}
Schedule* Pipeline::ComputeSchedule(ZonePool* zone_pool, Graph* graph) {
Schedule* schedule = Scheduler::ComputeSchedule(zone_pool, graph);
TraceSchedule(schedule);
if (VerifyGraphs()) ScheduleVerifier::Run(schedule);
return schedule;
}
Handle<Code> Pipeline::GenerateCodeForMachineGraph(Linkage* linkage,
Graph* graph,
Schedule* schedule) {
ZonePool zone_pool(isolate());
CHECK(SupportedBackend());
if (schedule == NULL) {
// TODO(rossberg): Should this really be untyped?
VerifyAndPrintGraph(graph, "Machine", true);
schedule = ComputeSchedule(&zone_pool, graph);
}
TraceSchedule(schedule);
SourcePositionTable source_positions(graph);
Handle<Code> 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());
OFStream os(tracing_scope.file());
code->Disassemble("test code", os);
}
#endif
return code;
}
Handle<Code> Pipeline::GenerateCode(PipelineStatistics* pipeline_statistics,
ZonePool* zone_pool, Linkage* linkage,
Graph* graph, Schedule* schedule,
SourcePositionTable* source_positions) {
DCHECK_NOT_NULL(graph);
DCHECK_NOT_NULL(linkage);
DCHECK_NOT_NULL(schedule);
CHECK(SupportedBackend());
BasicBlockProfiler::Data* profiler_data = NULL;
if (FLAG_turbo_profiling) {
profiler_data = BasicBlockInstrumentor::Instrument(info_, graph, schedule);
}
Zone* instruction_zone = schedule->zone();
InstructionSequence sequence(instruction_zone, graph, schedule);
// 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);
selector.SelectInstructions();
}
if (FLAG_trace_turbo) {
OFStream os(stdout);
os << "----- Instruction sequence before register allocation -----\n"
<< sequence;
TurboCfgFile tcf(isolate());
tcf << AsC1V("CodeGen", schedule, source_positions, &sequence);
}
// Allocate registers.
Frame frame;
{
int node_count = graph->NodeCount();
if (node_count > UnallocatedOperand::kMaxVirtualRegisters) {
linkage->info()->AbortOptimization(kNotEnoughVirtualRegistersForValues);
return Handle<Code>::null();
}
ZonePool::Scope zone_scope(zone_pool);
RegisterAllocator allocator(zone_scope.zone(), &frame, linkage->info(),
&sequence);
if (!allocator.Allocate(pipeline_statistics)) {
linkage->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;
}
// Generate native sequence.
Handle<Code> 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;
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