00edd9756b
Moves AllocateObject and CreateArray instructions down to their dominant use (use that dominates all other uses) when such a use exists and the move is not hampered by environment uses (which can happen when the allocation is inside a try block). This improves write barrier elimination for inlined constructors, since it moves the allocation after evaluation of the arguments. Any Dart calls in an argument would disable elimination after it. The optimization is particularly effective for Flutter Widget code, since such code typically contains many nested constructor calls. Reduces instructions size of Flutter Gallery by about 0.8%. Change-Id: Ife30850c1a23f0986f85d42c1015f4caa7cf1fa6 Cq-Do-Not-Cancel-Tryjobs: true Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/153602 Commit-Queue: Aske Simon Christensen <askesc@google.com> Reviewed-by: Martin Kustermann <kustermann@google.com>
208 lines
7.7 KiB
C++
208 lines
7.7 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.
|
|
|
|
#ifndef RUNTIME_VM_COMPILER_COMPILER_PASS_H_
|
|
#define RUNTIME_VM_COMPILER_COMPILER_PASS_H_
|
|
|
|
#if defined(DART_PRECOMPILED_RUNTIME)
|
|
#error "AOT runtime should not use compiler sources (including header files)"
|
|
#endif // defined(DART_PRECOMPILED_RUNTIME)
|
|
|
|
#include <initializer_list>
|
|
|
|
#include "vm/growable_array.h"
|
|
#include "vm/token_position.h"
|
|
#include "vm/zone.h"
|
|
|
|
namespace dart {
|
|
|
|
#define COMPILER_PASS_LIST(V) \
|
|
V(AllocateRegisters) \
|
|
V(AllocateRegistersForGraphIntrinsic) \
|
|
V(AllocationSinking_DetachMaterializations) \
|
|
V(AllocationSinking_Sink) \
|
|
V(ApplyClassIds) \
|
|
V(ApplyICData) \
|
|
V(BranchSimplify) \
|
|
V(CSE) \
|
|
V(Canonicalize) \
|
|
V(ComputeSSA) \
|
|
V(ConstantPropagation) \
|
|
V(DCE) \
|
|
V(DelayAllocations) \
|
|
V(DSE) \
|
|
V(EliminateDeadPhis) \
|
|
V(EliminateEnvironments) \
|
|
V(EliminateStackOverflowChecks) \
|
|
V(FinalizeGraph) \
|
|
V(IfConvert) \
|
|
V(Inlining) \
|
|
V(LICM) \
|
|
V(OptimisticallySpecializeSmiPhis) \
|
|
V(OptimizeBranches) \
|
|
V(OptimizeTypedDataAccesses) \
|
|
V(RangeAnalysis) \
|
|
V(ReorderBlocks) \
|
|
V(RoundTripSerialization) \
|
|
V(SelectRepresentations) \
|
|
V(SerializeGraph) \
|
|
V(SetOuterInliningId) \
|
|
V(TryCatchOptimization) \
|
|
V(TryOptimizePatterns) \
|
|
V(TypePropagation) \
|
|
V(UseTableDispatch) \
|
|
V(WidenSmiToInt32) \
|
|
V(EliminateWriteBarriers)
|
|
|
|
class AllocationSinking;
|
|
class BlockScheduler;
|
|
class CallSpecializer;
|
|
class FlowGraph;
|
|
class Function;
|
|
class Precompiler;
|
|
class SpeculativeInliningPolicy;
|
|
class TimelineStream;
|
|
class Thread;
|
|
|
|
struct CompilerPassState {
|
|
CompilerPassState(Thread* thread,
|
|
FlowGraph* flow_graph,
|
|
SpeculativeInliningPolicy* speculative_policy,
|
|
Precompiler* precompiler = NULL)
|
|
: thread(thread),
|
|
precompiler(precompiler),
|
|
inlining_depth(0),
|
|
sinking(NULL),
|
|
call_specializer(NULL),
|
|
speculative_policy(speculative_policy),
|
|
reorder_blocks(false),
|
|
sticky_flags(0),
|
|
flow_graph_(flow_graph) {}
|
|
|
|
FlowGraph* flow_graph() const { return flow_graph_; }
|
|
|
|
void set_flow_graph(FlowGraph* flow_graph);
|
|
|
|
Thread* const thread;
|
|
Precompiler* const precompiler;
|
|
int inlining_depth;
|
|
AllocationSinking* sinking;
|
|
|
|
// Maps inline_id_to_function[inline_id] -> function. Top scope
|
|
// function has inline_id 0. The map is populated by the inliner.
|
|
GrowableArray<const Function*> inline_id_to_function;
|
|
// Token position where inlining occured.
|
|
GrowableArray<TokenPosition> inline_id_to_token_pos;
|
|
// For a given inlining-id(index) specifies the caller's inlining-id.
|
|
GrowableArray<intptr_t> caller_inline_id;
|
|
|
|
CallSpecializer* call_specializer;
|
|
|
|
SpeculativeInliningPolicy* speculative_policy;
|
|
|
|
bool reorder_blocks;
|
|
|
|
intptr_t sticky_flags;
|
|
|
|
private:
|
|
FlowGraph* flow_graph_;
|
|
};
|
|
|
|
class CompilerPass {
|
|
public:
|
|
enum Id {
|
|
#define DEF(name) k##name,
|
|
COMPILER_PASS_LIST(DEF)
|
|
#undef DEF
|
|
};
|
|
|
|
#define ADD_ONE(name) +1
|
|
static constexpr intptr_t kNumPasses = 0 COMPILER_PASS_LIST(ADD_ONE);
|
|
#undef ADD_ONE
|
|
|
|
CompilerPass(Id id, const char* name) : name_(name), flags_(0) {
|
|
ASSERT(passes_[id] == NULL);
|
|
passes_[id] = this;
|
|
|
|
// By default print the final flow-graph after the register allocation.
|
|
if (id == kAllocateRegisters) {
|
|
flags_ = kTraceAfter;
|
|
}
|
|
}
|
|
virtual ~CompilerPass() {}
|
|
|
|
enum Flag {
|
|
kDisabled = 1 << 0,
|
|
kTraceBefore = 1 << 1,
|
|
kTraceAfter = 1 << 2,
|
|
kSticky = 1 << 3,
|
|
kTraceBeforeOrAfter = kTraceBefore | kTraceAfter,
|
|
};
|
|
|
|
void Run(CompilerPassState* state) const;
|
|
|
|
intptr_t flags() const { return flags_; }
|
|
const char* name() const { return name_; }
|
|
|
|
bool IsFlagSet(Flag flag) const { return (flags() & flag) != 0; }
|
|
|
|
static CompilerPass* Get(Id id) { return passes_[id]; }
|
|
|
|
static void ParseFilters(const char* filter);
|
|
|
|
enum PipelineMode { kJIT, kAOT };
|
|
|
|
static void RunGraphIntrinsicPipeline(CompilerPassState* state);
|
|
|
|
static void RunInliningPipeline(PipelineMode mode, CompilerPassState* state);
|
|
|
|
// RunPipeline(WithPasses) may have the side effect of changing the FlowGraph
|
|
// stored in the CompilerPassState. However, existing callers may depend on
|
|
// the old invariant that the FlowGraph stored in the CompilerPassState was
|
|
// always updated, never entirely replaced.
|
|
//
|
|
// To make sure callers are updated properly, these methods also return
|
|
// the final FlowGraph and we add a check that callers use this result.
|
|
DART_WARN_UNUSED_RESULT
|
|
static FlowGraph* RunPipeline(PipelineMode mode, CompilerPassState* state);
|
|
DART_WARN_UNUSED_RESULT
|
|
static FlowGraph* RunPipelineWithPasses(
|
|
CompilerPassState* state,
|
|
std::initializer_list<CompilerPass::Id> passes);
|
|
|
|
// Pipeline which is used for "force-optimized" functions.
|
|
//
|
|
// Must not include speculative or inter-procedural optimizations.
|
|
DART_WARN_UNUSED_RESULT
|
|
static FlowGraph* RunForceOptimizedPipeline(PipelineMode mode,
|
|
CompilerPassState* state);
|
|
|
|
protected:
|
|
// This function executes the pass. If it returns true then
|
|
// we will run Canonicalize on the graph and execute the pass
|
|
// again.
|
|
virtual bool DoBody(CompilerPassState* state) const = 0;
|
|
|
|
private:
|
|
static CompilerPass* FindPassByName(const char* name) {
|
|
for (intptr_t i = 0; i < kNumPasses; i++) {
|
|
if ((passes_[i] != NULL) && (strcmp(passes_[i]->name_, name) == 0)) {
|
|
return passes_[i];
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void PrintGraph(CompilerPassState* state, Flag mask, intptr_t round) const;
|
|
|
|
static CompilerPass* passes_[];
|
|
|
|
const char* name_;
|
|
intptr_t flags_;
|
|
};
|
|
|
|
} // namespace dart
|
|
|
|
#endif // RUNTIME_VM_COMPILER_COMPILER_PASS_H_
|