// 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 // NOLINT(readability/streams) #include #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/select-lowering.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 source_positions_; MachineOperatorBuilder* machine_; CommonOperatorBuilder* common_; JSOperatorBuilder* javascript_; JSGraph* jsgraph_; // TODO(dcarney): make this into a ZoneObject. SmartPointer 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 filename(buffer, sizeof(buffer)); SmartArrayPointer 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(info_), phase); } } else { SNPrintF(filename, "turbo-none-%s", phase); } std::replace(filename.start(), filename.start() + filename.length(), ' ', '_'); char dot_buffer[256]; Vector 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 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 GetDebugName(CompilationInfo* info) { SmartArrayPointer 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 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::null(); } ZonePool zone_pool(isolate()); SmartPointer 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()); if (!graph_builder.CreateGraph()) return Handle::null(); 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::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 generic(info(), data.jsgraph()); SelectLowering select(data.jsgraph()->graph(), data.jsgraph()->common()); GraphReducer graph_reducer(data.graph()); graph_reducer.AddReducer(&generic); graph_reducer.AddReducer(&select); 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 = Handle::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 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 = 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 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()); } InstructionBlocks* instruction_blocks = InstructionSequence::InstructionBlocksFor(data->instruction_zone(), data->schedule()); InstructionSequence sequence(data->instruction_zone(), instruction_blocks); // 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); PrintableInstructionSequence printable = { RegisterConfiguration::ArchDefault(), &sequence}; os << "----- Instruction sequence before register allocation -----\n" << printable; 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::null(); } ZonePool::Scope zone_scope(data->zone_pool()); SmartArrayPointer debug_name; #ifdef DEBUG debug_name = GetDebugName(info()); #endif RegisterAllocator allocator(RegisterConfiguration::ArchDefault(), zone_scope.zone(), &frame, &sequence, debug_name.get()); if (!allocator.Allocate(data->pipeline_statistics())) { info()->AbortOptimization(kNotEnoughVirtualRegistersRegalloc); return Handle::null(); } if (FLAG_trace_turbo) { TurboCfgFile tcf(isolate()); tcf << AsC1VAllocator("CodeGen", &allocator); } } if (FLAG_trace_turbo) { OFStream os(stdout); PrintableInstructionSequence printable = { RegisterConfiguration::ArchDefault(), &sequence}; os << "----- Instruction sequence after register allocation -----\n" << printable; } if (data->pipeline_statistics() != NULL) { data->pipeline_statistics()->BeginPhaseKind("code generation"); } // Generate native sequence. Handle 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