// 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(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 filename(buffer, sizeof(buffer)); if (!info_->shared_info().is_null()) { SmartArrayPointer 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(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 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::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 = Handle::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 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 = 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 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()->AbortOptimization(kNotEnoughVirtualRegistersForValues); return Handle::null(); } RegisterAllocator allocator(&sequence); if (!allocator.Allocate()) { linkage->info()->AbortOptimization(kNotEnoughVirtualRegistersRegalloc); return Handle::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