6abaa310e0
After https://dart-review.googlesource.com/c/sdk/+/356311, GraphEntryInstr::catch_entries() is no longer needed and can be replaced with FlowGraph::try_entries(). This change also fixes crash during IL deserialization if flow graph contains a catch block. TEST=vm/dart/checked_parameter_assert_assignable_stacktrace_test Change-Id: Iad398e142bbd00110457e3b121bd446c32a5ac46 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/401860 Commit-Queue: Alexander Markov <alexmarkov@google.com> Reviewed-by: Alexander Aprelev <aam@google.com>
571 lines
20 KiB
C++
571 lines
20 KiB
C++
// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
#include "vm/compiler/compiler_pass.h"
|
|
|
|
#include "vm/compiler/backend/block_scheduler.h"
|
|
#include "vm/compiler/backend/branch_optimizer.h"
|
|
#include "vm/compiler/backend/constant_propagator.h"
|
|
#include "vm/compiler/backend/flow_graph_checker.h"
|
|
#include "vm/compiler/backend/flow_graph_compiler.h"
|
|
#include "vm/compiler/backend/il_printer.h"
|
|
#include "vm/compiler/backend/inliner.h"
|
|
#include "vm/compiler/backend/linearscan.h"
|
|
#include "vm/compiler/backend/range_analysis.h"
|
|
#include "vm/compiler/backend/redundancy_elimination.h"
|
|
#include "vm/compiler/backend/type_propagator.h"
|
|
#include "vm/compiler/call_specializer.h"
|
|
#include "vm/compiler/compiler_timings.h"
|
|
#include "vm/compiler/write_barrier_elimination.h"
|
|
#if defined(DART_PRECOMPILER)
|
|
#include "vm/compiler/aot/aot_call_specializer.h"
|
|
#include "vm/compiler/aot/precompiler.h"
|
|
#endif
|
|
#include "vm/thread.h"
|
|
#include "vm/timeline.h"
|
|
|
|
#define COMPILER_PASS_REPEAT(Name, Body) \
|
|
class CompilerPass_##Name : public CompilerPass { \
|
|
public: \
|
|
CompilerPass_##Name() : CompilerPass(k##Name, #Name) {} \
|
|
\
|
|
static bool Register() { \
|
|
return true; \
|
|
} \
|
|
\
|
|
protected: \
|
|
virtual bool DoBody(CompilerPassState* state) const { \
|
|
FlowGraph* flow_graph = state->flow_graph(); \
|
|
USE(flow_graph); \
|
|
Body; \
|
|
} \
|
|
}; \
|
|
static CompilerPass_##Name compiler_pass_##Name;
|
|
|
|
#define COMPILER_PASS(Name, Body) \
|
|
COMPILER_PASS_REPEAT(Name, { \
|
|
Body; \
|
|
return false; \
|
|
})
|
|
|
|
namespace dart {
|
|
|
|
CompilerPassState::CompilerPassState(Thread* thread,
|
|
FlowGraph* flow_graph,
|
|
Precompiler* precompiler)
|
|
: thread(thread),
|
|
precompiler(precompiler),
|
|
inlining_depth(0),
|
|
sinking(nullptr),
|
|
call_specializer(nullptr),
|
|
sticky_flags(0),
|
|
flow_graph_(flow_graph) {}
|
|
|
|
CompilerPass* CompilerPass::passes_[CompilerPass::kNumPasses] = {nullptr};
|
|
uint8_t CompilerPass::flags_[CompilerPass::kNumPasses] = {0};
|
|
|
|
DEFINE_OPTION_HANDLER(CompilerPass::ParseFiltersFromFlag,
|
|
compiler_passes,
|
|
"List of comma separated compilation passes flags. "
|
|
"Use -Name to disable a pass, Name to print IL after it. "
|
|
"Do --compiler-passes=help for more information.");
|
|
DECLARE_FLAG(bool, print_flow_graph);
|
|
DECLARE_FLAG(bool, print_flow_graph_optimized);
|
|
DEFINE_FLAG(bool, test_il_serialization, false, "Test IL serialization.");
|
|
|
|
void CompilerPassState::set_flow_graph(FlowGraph* flow_graph) {
|
|
flow_graph_ = flow_graph;
|
|
if (call_specializer != nullptr) {
|
|
call_specializer->set_flow_graph(flow_graph);
|
|
}
|
|
}
|
|
|
|
static const char* kCompilerPassesUsage =
|
|
"=== How to use --compiler-passes flag\n"
|
|
"\n"
|
|
"Pass the list of comma separated compiler pass filter flags.\n"
|
|
"\n"
|
|
"For the given pass Name the following flags are supported:\n"
|
|
"\n"
|
|
" -Name disable the pass\n"
|
|
" ]Name or Name print IL after the pass\n"
|
|
" [Name print IL before the pass\n"
|
|
" *Name print IL before and after the pass\n"
|
|
" * print IL after each pass.\n"
|
|
"\n"
|
|
" The flag can be followed by '+' which makes it sticky, e.g. Inlining+\n"
|
|
" would cause IL to be printed after all passes that follow inlining and\n"
|
|
" are not disabled.\n"
|
|
"\n"
|
|
"List of compiler passes:\n";
|
|
|
|
void CompilerPass::ParseFiltersFromFlag(const char* filter) {
|
|
ParseFilters(filter, flags_);
|
|
}
|
|
|
|
uint8_t* CompilerPass::ParseFiltersFromPragma(const char* filter) {
|
|
auto flags =
|
|
ThreadState::Current()->zone()->Alloc<uint8_t>(CompilerPass::kNumPasses);
|
|
ParseFilters(filter, flags);
|
|
return flags;
|
|
}
|
|
|
|
void CompilerPass::ParseFilters(const char* filter, uint8_t* pass_flags) {
|
|
if (filter == nullptr || *filter == 0) {
|
|
return;
|
|
}
|
|
|
|
if (strcmp(filter, "help") == 0) {
|
|
OS::PrintErr("%s", kCompilerPassesUsage);
|
|
for (intptr_t i = 0; i < kNumPasses; i++) {
|
|
if (passes_[i] != nullptr) {
|
|
OS::PrintErr(" %s\n", passes_[i]->name());
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Clear all flags.
|
|
memset(pass_flags, 0, CompilerPass::kNumPasses);
|
|
|
|
for (const char *start = filter, *end = filter; *end != 0;
|
|
start = (end + 1)) {
|
|
// Search forward until the separator ',' or the end of filter is reached.
|
|
end = start;
|
|
while (*end != ',' && *end != '\0') {
|
|
end++;
|
|
}
|
|
if (start == end) {
|
|
OS::PrintErr("Ignoring empty compiler pass flag\n");
|
|
continue;
|
|
}
|
|
|
|
ParseOneFilter(start, end, pass_flags);
|
|
}
|
|
}
|
|
|
|
void CompilerPass::ParseOneFilter(const char* start,
|
|
const char* end,
|
|
uint8_t* pass_flags) {
|
|
uint8_t flags = 0;
|
|
if (*start == '-') {
|
|
flags = kDisabled;
|
|
} else if (*start == ']') {
|
|
flags = kTraceAfter;
|
|
} else if (*start == '[') {
|
|
flags = kTraceBefore;
|
|
} else if (*start == '*') {
|
|
flags = kTraceBeforeOrAfter;
|
|
}
|
|
if (flags == 0) {
|
|
flags |= kTraceAfter;
|
|
} else {
|
|
start++; // Skip the modifier
|
|
}
|
|
|
|
size_t suffix = 0;
|
|
if (end[-1] == '+') {
|
|
if (start == (end - 1)) {
|
|
OS::PrintErr("Sticky modifier '+' should follow pass name\n");
|
|
return;
|
|
}
|
|
flags |= kSticky;
|
|
suffix = 1;
|
|
}
|
|
|
|
size_t length = (end - start) - suffix;
|
|
if (length != 0) {
|
|
char* pass_name = Utils::StrNDup(start, length);
|
|
CompilerPass* pass = FindPassByName(pass_name);
|
|
if (pass != nullptr) {
|
|
pass_flags[pass->id()] |= flags;
|
|
} else {
|
|
OS::PrintErr("Unknown compiler pass: %s\n", pass_name);
|
|
}
|
|
free(pass_name);
|
|
} else if (flags == kTraceBeforeOrAfter) {
|
|
for (intptr_t i = 0; i < kNumPasses; i++) {
|
|
pass_flags[i] = kTraceAfter;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CompilerPass::Run(CompilerPassState* state) const {
|
|
if ((flags() & kDisabled) != 0) {
|
|
return;
|
|
}
|
|
|
|
if ((flags() & kSticky) != 0) {
|
|
state->sticky_flags |= flags();
|
|
}
|
|
|
|
const intptr_t kMaxRounds = 2;
|
|
Thread* thread = state->thread;
|
|
bool repeat = true;
|
|
for (intptr_t round = 1; round <= kMaxRounds && repeat; round++) {
|
|
if (round > 1) {
|
|
Get(kCanonicalize)->Run(state);
|
|
}
|
|
|
|
CompilerState::Current().set_current_pass(this, state);
|
|
PrintGraph(state, kTraceBefore, round);
|
|
{
|
|
TIMELINE_DURATION(thread, CompilerVerbose, name());
|
|
{
|
|
COMPILER_TIMINGS_PASS_TIMER_SCOPE(thread, id());
|
|
repeat = DoBody(state);
|
|
}
|
|
thread->CheckForSafepoint();
|
|
}
|
|
PrintGraph(state, kTraceAfter, round);
|
|
#if defined(DEBUG)
|
|
if (CompilerState::Current().is_optimizing()) {
|
|
FlowGraphChecker(state->flow_graph()).Check(name());
|
|
}
|
|
#endif
|
|
CompilerState::Current().set_current_pass(nullptr, nullptr);
|
|
}
|
|
}
|
|
|
|
void CompilerPass::PrintGraph(CompilerPassState* state,
|
|
Flag mask,
|
|
intptr_t round) const {
|
|
FlowGraph* flow_graph = state->flow_graph();
|
|
const uint8_t* graph_flags = flow_graph->compiler_pass_filters();
|
|
const uint8_t current_flags =
|
|
(graph_flags != nullptr ? graph_flags[id()] : flags()) |
|
|
state->sticky_flags;
|
|
|
|
if ((FLAG_print_flow_graph || FLAG_print_flow_graph_optimized) &&
|
|
flow_graph->should_print() && ((current_flags & mask) != 0)) {
|
|
Zone* zone = state->thread->zone();
|
|
const char* when = mask == kTraceBefore ? "Before" : "After";
|
|
const char* phase =
|
|
round == 1
|
|
? zone->PrintToString("%s %s", when, name())
|
|
: zone->PrintToString("%s %s (round %" Pd ")", when, name(), round);
|
|
|
|
FlowGraphPrinter::PrintGraph(phase, flow_graph);
|
|
}
|
|
}
|
|
|
|
#define INVOKE_PASS(Name) \
|
|
CompilerPass::Get(CompilerPass::k##Name)->Run(pass_state);
|
|
|
|
#if defined(DART_PRECOMPILER)
|
|
#define INVOKE_PASS_AOT(Name) \
|
|
if (mode == kAOT) { \
|
|
INVOKE_PASS(Name); \
|
|
}
|
|
#else
|
|
#define INVOKE_PASS_AOT(Name)
|
|
#endif
|
|
|
|
void CompilerPass::RunGraphIntrinsicPipeline(CompilerPassState* pass_state) {
|
|
INVOKE_PASS(AllocateRegistersForGraphIntrinsic);
|
|
}
|
|
|
|
void CompilerPass::RunInliningPipeline(PipelineMode mode,
|
|
CompilerPassState* pass_state) {
|
|
INVOKE_PASS(ApplyClassIds);
|
|
INVOKE_PASS(TypePropagation);
|
|
INVOKE_PASS(ApplyICData);
|
|
INVOKE_PASS(Canonicalize);
|
|
// Run constant propagation to make sure we specialize for
|
|
// (optional) constant arguments passed into the inlined method.
|
|
INVOKE_PASS(ConstantPropagation);
|
|
// Constant propagation removes unreachable basic blocks and
|
|
// may open more opportunities for call specialization.
|
|
// Call specialization during inlining may cause more call
|
|
// sites to be discovered and more functions inlined.
|
|
INVOKE_PASS_AOT(ApplyClassIds);
|
|
// Optimize (a << b) & c patterns, merge instructions. Must occur
|
|
// before 'SelectRepresentations' which inserts conversion nodes.
|
|
INVOKE_PASS(TryOptimizePatterns);
|
|
}
|
|
|
|
FlowGraph* CompilerPass::RunPipeline(PipelineMode mode,
|
|
CompilerPassState* pass_state,
|
|
bool compute_ssa) {
|
|
if (compute_ssa) {
|
|
INVOKE_PASS(ComputeSSA);
|
|
}
|
|
INVOKE_PASS_AOT(ApplyClassIds);
|
|
INVOKE_PASS_AOT(TypePropagation);
|
|
INVOKE_PASS(ApplyICData);
|
|
INVOKE_PASS(TryOptimizePatterns);
|
|
INVOKE_PASS(SetOuterInliningId);
|
|
INVOKE_PASS(TypePropagation);
|
|
INVOKE_PASS(ApplyClassIds);
|
|
INVOKE_PASS(Inlining);
|
|
INVOKE_PASS(TypePropagation);
|
|
INVOKE_PASS(ApplyClassIds);
|
|
INVOKE_PASS(TypePropagation);
|
|
INVOKE_PASS(ApplyICData);
|
|
INVOKE_PASS(Canonicalize);
|
|
INVOKE_PASS(BranchSimplify);
|
|
INVOKE_PASS(IfConvert);
|
|
INVOKE_PASS(Canonicalize);
|
|
INVOKE_PASS(ConstantPropagation);
|
|
INVOKE_PASS(OptimisticallySpecializeSmiPhis);
|
|
INVOKE_PASS(TypePropagation);
|
|
// The extra call specialization pass in AOT is able to specialize more
|
|
// calls after ConstantPropagation, which removes unreachable code, and
|
|
// TypePropagation, which can infer more accurate types after removing
|
|
// unreachable code.
|
|
INVOKE_PASS_AOT(ApplyICData);
|
|
INVOKE_PASS_AOT(OptimizeTypedDataAccesses);
|
|
INVOKE_PASS(SelectRepresentations);
|
|
INVOKE_PASS(CSE);
|
|
INVOKE_PASS(Canonicalize);
|
|
INVOKE_PASS(LICM);
|
|
INVOKE_PASS(TryOptimizePatterns);
|
|
INVOKE_PASS(DSE);
|
|
INVOKE_PASS(TypePropagation);
|
|
INVOKE_PASS(RangeAnalysis);
|
|
INVOKE_PASS(OptimizeBranches);
|
|
INVOKE_PASS(TypePropagation);
|
|
INVOKE_PASS(TryCatchOptimization);
|
|
INVOKE_PASS(EliminateEnvironments);
|
|
INVOKE_PASS(EliminateDeadPhis);
|
|
// Currently DCE assumes that EliminateEnvironments has already been run,
|
|
// so it should not be lifted earlier than that pass.
|
|
INVOKE_PASS(DCE);
|
|
INVOKE_PASS(Canonicalize);
|
|
INVOKE_PASS_AOT(DelayAllocations);
|
|
// Repeat branches optimization after DCE, as it could make more
|
|
// empty blocks.
|
|
INVOKE_PASS(OptimizeBranches);
|
|
INVOKE_PASS(AllocationSinking_Sink);
|
|
INVOKE_PASS(EliminateDeadPhis);
|
|
INVOKE_PASS(DCE);
|
|
INVOKE_PASS(Canonicalize);
|
|
INVOKE_PASS(TypePropagation);
|
|
INVOKE_PASS(SelectRepresentations_Final);
|
|
INVOKE_PASS(UseTableDispatch);
|
|
INVOKE_PASS(EliminateStackOverflowChecks);
|
|
INVOKE_PASS(Canonicalize);
|
|
INVOKE_PASS(AllocationSinking_DetachMaterializations);
|
|
INVOKE_PASS(EliminateWriteBarriers);
|
|
// This must be done after all other possible intra-block code motion.
|
|
INVOKE_PASS(LoweringAfterCodeMotionDisabled);
|
|
INVOKE_PASS(FinalizeGraph);
|
|
INVOKE_PASS(Canonicalize);
|
|
INVOKE_PASS(ReorderBlocks);
|
|
INVOKE_PASS(AllocateRegisters);
|
|
INVOKE_PASS(TestILSerialization); // Must be last.
|
|
return pass_state->flow_graph();
|
|
}
|
|
|
|
FlowGraph* CompilerPass::RunPipelineWithPasses(
|
|
CompilerPassState* state,
|
|
std::initializer_list<CompilerPass::Id> passes) {
|
|
for (auto pass_id : passes) {
|
|
passes_[pass_id]->Run(state);
|
|
}
|
|
return state->flow_graph();
|
|
}
|
|
|
|
COMPILER_PASS(ComputeSSA, {
|
|
// Transform to SSA (no inlining arguments).
|
|
flow_graph->ComputeSSA(nullptr);
|
|
});
|
|
|
|
COMPILER_PASS(ApplyICData, { state->call_specializer->ApplyICData(); });
|
|
|
|
COMPILER_PASS(TryOptimizePatterns, { flow_graph->TryOptimizePatterns(); });
|
|
|
|
COMPILER_PASS(SetOuterInliningId, {
|
|
FlowGraphInliner::SetInliningIdAndTryIndex(flow_graph, 0, kInvalidTryIndex);
|
|
});
|
|
|
|
COMPILER_PASS(Inlining, {
|
|
FlowGraphInliner inliner(flow_graph, state->precompiler);
|
|
state->inlining_depth = inliner.Inline();
|
|
});
|
|
|
|
COMPILER_PASS(TypePropagation,
|
|
{ FlowGraphTypePropagator::Propagate(flow_graph); });
|
|
|
|
COMPILER_PASS(ApplyClassIds, { state->call_specializer->ApplyClassIds(); });
|
|
|
|
COMPILER_PASS(EliminateStackOverflowChecks, {
|
|
if (!flow_graph->IsCompiledForOsr()) {
|
|
CheckStackOverflowElimination::EliminateStackOverflow(flow_graph);
|
|
}
|
|
});
|
|
|
|
COMPILER_PASS(Canonicalize, {
|
|
// Do optimizations that depend on the propagated type information.
|
|
if (flow_graph->Canonicalize()) {
|
|
flow_graph->Canonicalize();
|
|
}
|
|
});
|
|
|
|
COMPILER_PASS(BranchSimplify, { BranchSimplifier::Simplify(flow_graph); });
|
|
|
|
COMPILER_PASS(IfConvert, { IfConverter::Simplify(flow_graph); });
|
|
|
|
COMPILER_PASS_REPEAT(ConstantPropagation, {
|
|
ConstantPropagator::Optimize(flow_graph);
|
|
return true;
|
|
});
|
|
|
|
// Optimistically convert loop phis that have a single non-smi input
|
|
// coming from the loop pre-header into smi-phis.
|
|
COMPILER_PASS(OptimisticallySpecializeSmiPhis, {
|
|
LICM licm(flow_graph);
|
|
licm.OptimisticallySpecializeSmiPhis();
|
|
});
|
|
|
|
COMPILER_PASS(SelectRepresentations, {
|
|
// Unbox doubles. Performed after constant propagation to minimize
|
|
// interference from phis merging double values and tagged
|
|
// values coming from dead paths.
|
|
flow_graph->SelectRepresentations();
|
|
});
|
|
|
|
COMPILER_PASS(SelectRepresentations_Final, {
|
|
// Final selection of representations. After this pass
|
|
// representations of inputs/outputs should match.
|
|
flow_graph->SelectRepresentations();
|
|
flow_graph->disallow_unmatched_representations();
|
|
});
|
|
|
|
COMPILER_PASS(UseTableDispatch, {
|
|
state->call_specializer->ReplaceInstanceCallsWithDispatchTableCalls();
|
|
});
|
|
|
|
COMPILER_PASS_REPEAT(CSE, { return DominatorBasedCSE::Optimize(flow_graph); });
|
|
|
|
COMPILER_PASS(LICM, {
|
|
if (flow_graph->is_huge_method()) {
|
|
return false; // Runs in quadratic time.
|
|
}
|
|
|
|
flow_graph->RenameUsesDominatedByRedefinitions();
|
|
DEBUG_ASSERT(flow_graph->VerifyRedefinitions());
|
|
LICM licm(flow_graph);
|
|
licm.Optimize();
|
|
flow_graph->RemoveRedefinitions(/*keep_checks*/ true);
|
|
});
|
|
|
|
COMPILER_PASS(DSE, { DeadStoreElimination::Optimize(flow_graph); });
|
|
|
|
COMPILER_PASS(RangeAnalysis, {
|
|
if (flow_graph->is_huge_method()) {
|
|
return false; // Runs in quadratic time.
|
|
}
|
|
|
|
// We have to perform range analysis after LICM because it
|
|
// optimistically moves CheckSmi through phis into loop preheaders
|
|
// making some phis smi.
|
|
RangeAnalysis range_analysis(flow_graph);
|
|
range_analysis.Analyze();
|
|
});
|
|
|
|
COMPILER_PASS(OptimizeBranches, {
|
|
// Constant propagation can use information from range analysis to
|
|
// find unreachable branch targets and eliminate branches that have
|
|
// the same true- and false-target.
|
|
ConstantPropagator::OptimizeBranches(flow_graph);
|
|
});
|
|
|
|
COMPILER_PASS(OptimizeTypedDataAccesses,
|
|
{ TypedDataSpecializer::Optimize(flow_graph); });
|
|
|
|
COMPILER_PASS(TryCatchOptimization, {
|
|
OptimizeCatchEntryStates(flow_graph,
|
|
/*is_aot=*/CompilerState::Current().is_aot());
|
|
});
|
|
|
|
COMPILER_PASS(EliminateEnvironments, { flow_graph->EliminateEnvironments(); });
|
|
|
|
COMPILER_PASS(EliminateDeadPhis,
|
|
{ DeadCodeElimination::EliminateDeadPhis(flow_graph); });
|
|
|
|
COMPILER_PASS(DCE, { DeadCodeElimination::EliminateDeadCode(flow_graph); });
|
|
|
|
COMPILER_PASS(DelayAllocations, { DelayAllocations::Optimize(flow_graph); });
|
|
|
|
COMPILER_PASS(AllocationSinking_Sink, {
|
|
// TODO(vegorov): Support allocation sinking with try-catch.
|
|
if (flow_graph->try_entries().is_empty()) {
|
|
state->sinking = new AllocationSinking(flow_graph);
|
|
state->sinking->Optimize();
|
|
}
|
|
});
|
|
|
|
COMPILER_PASS(AllocationSinking_DetachMaterializations, {
|
|
if (state->sinking != nullptr) {
|
|
// Remove all MaterializeObject instructions inserted by allocation
|
|
// sinking from the flow graph and let them float on the side
|
|
// referenced only from environments. Register allocator will consider
|
|
// them as part of a deoptimization environment.
|
|
state->sinking->DetachMaterializations();
|
|
}
|
|
});
|
|
|
|
COMPILER_PASS(AllocateRegisters, {
|
|
flow_graph->InsertMoveArguments();
|
|
// Ensure loop hierarchy has been computed.
|
|
flow_graph->GetLoopHierarchy();
|
|
// Perform register allocation on the SSA graph.
|
|
FlowGraphAllocator allocator(*flow_graph);
|
|
allocator.AllocateRegisters();
|
|
});
|
|
|
|
COMPILER_PASS(AllocateRegistersForGraphIntrinsic, {
|
|
flow_graph->set_max_argument_slot_count(0);
|
|
// Ensure loop hierarchy has been computed.
|
|
flow_graph->GetLoopHierarchy();
|
|
// Perform register allocation on the SSA graph.
|
|
FlowGraphAllocator allocator(*flow_graph, /*intrinsic_mode=*/true);
|
|
allocator.AllocateRegisters();
|
|
});
|
|
|
|
COMPILER_PASS(ReorderBlocks, { BlockScheduler::ReorderBlocks(flow_graph); });
|
|
|
|
COMPILER_PASS(EliminateWriteBarriers, { EliminateWriteBarriers(flow_graph); });
|
|
|
|
COMPILER_PASS(FinalizeGraph, {
|
|
// At the end of the pipeline, force recomputing and caching graph
|
|
// information (instruction and call site counts) for the (assumed)
|
|
// non-specialized case with better values, for future inlining.
|
|
intptr_t instruction_count = 0;
|
|
intptr_t call_site_count = 0;
|
|
FlowGraphInliner::CollectGraphInfo(flow_graph,
|
|
/*constants_count*/ 0,
|
|
/*force*/ true, &instruction_count,
|
|
&call_site_count);
|
|
flow_graph->function().set_inlining_depth(state->inlining_depth);
|
|
// Remove redefinitions for the rest of the pipeline.
|
|
flow_graph->RemoveRedefinitions();
|
|
});
|
|
|
|
COMPILER_PASS(TestILSerialization, {
|
|
// This is the last compiler pass.
|
|
// Test that round-trip IL serialization works before generating code.
|
|
if (FLAG_test_il_serialization && CompilerState::Current().is_aot()) {
|
|
Zone* zone = flow_graph->zone();
|
|
auto* detached_defs = new (zone) ZoneGrowableArray<Definition*>(zone, 0);
|
|
flow_graph->CompactSSA(detached_defs);
|
|
|
|
ZoneWriteStream write_stream(flow_graph->zone(), 1024);
|
|
FlowGraphSerializer serializer(&write_stream);
|
|
serializer.WriteFlowGraph(*flow_graph, *detached_defs);
|
|
ReadStream read_stream(write_stream.buffer(), write_stream.bytes_written());
|
|
FlowGraphDeserializer deserializer(flow_graph->parsed_function(),
|
|
&read_stream);
|
|
state->set_flow_graph(deserializer.ReadFlowGraph());
|
|
}
|
|
});
|
|
|
|
COMPILER_PASS(LoweringAfterCodeMotionDisabled,
|
|
{ flow_graph->ExtractNonInternalTypedDataPayloads(); });
|
|
|
|
COMPILER_PASS(GenerateCode, { state->graph_compiler->CompileGraph(); });
|
|
|
|
} // namespace dart
|