674a42d292
Inform ASAN and MSAN about reads and writes through FFI pointers or external typed data. Previously we only informed MSAN about writes, which was enough to prevent false positives in foreign code, but gave false negatives for Dart code. This mostly uses the same machinery used for TSAN, and replaces some of the existing MSAN unpoison calls. Also fixes some marshalling of compounds by value that generate loads extending past the end of the compound. TEST=ci Bug: https://github.com/dart-lang/sdk/issues/62221 Change-Id: Ia3573edb5a0aec32b6a57035a63e8f323a655ecc Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/467401 Reviewed-by: Daco Harkes <dacoharkes@google.com> Commit-Queue: Ryan Macnak <rmacnak@google.com>
572 lines
20 KiB
C++
572 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();
|
|
flow_graph->AddAsanMsanInstrumentation();
|
|
flow_graph->AddTsanInstrumentation();
|
|
});
|
|
|
|
COMPILER_PASS(GenerateCode, { state->graph_compiler->CompileGraph(); });
|
|
|
|
} // namespace dart
|