ba0f59d1bc
The optimizing compiler currently recognizes a certain frequent native methods like array length or string length and provides an inlined implementation. Inlining does currently not work for polymorphic call sites of these methods. This CL enables also polymorphic inlining in the case of .length getters for arrays and strings. 1. The method is recognized at flow graph build time. The builder creates the body of the method for both compilers (non-optimizing and optimizing). Native methods that are not recognized, are translated as before using a NativeCall IL instruction. 2. The flow graph inliner handles recognized methods in the same manner as normal methods. Until now intrinsic and recognized method could not be inlined. This CL enables it. 3. There is no need for an intrinsic assembly implementation because recognized methods have an IL implementation that does not call into the C++ runtime. I left the intrinsics in for now, but they can be removed if there is not noticable performance benefit anymore. 4. The inlining heuristics are tweaked in a way that enables more aggressive inlining of recognized methods: +1 level of inlining depths, call sites of recognized methods are not counted in the inlining heuristic. R=kmillikin@google.com, srdjan@google.com Review URL: https://codereview.chromium.org//22839003 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@26429 260f80e4-7a28-3924-810f-c04153c831b5
383 lines
13 KiB
C++
383 lines
13 KiB
C++
// Copyright (c) 2012, 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 VM_FLOW_GRAPH_OPTIMIZER_H_
|
|
#define VM_FLOW_GRAPH_OPTIMIZER_H_
|
|
|
|
#include "vm/intermediate_language.h"
|
|
#include "vm/flow_graph.h"
|
|
|
|
namespace dart {
|
|
|
|
class CSEInstructionMap;
|
|
template <typename T> class GrowableArray;
|
|
class ParsedFunction;
|
|
|
|
class FlowGraphOptimizer : public FlowGraphVisitor {
|
|
public:
|
|
FlowGraphOptimizer(FlowGraph* flow_graph,
|
|
GrowableArray<const Field*>* guarded_fields)
|
|
: FlowGraphVisitor(flow_graph->reverse_postorder()),
|
|
flow_graph_(flow_graph),
|
|
guarded_fields_(guarded_fields) { }
|
|
virtual ~FlowGraphOptimizer() {}
|
|
|
|
FlowGraph* flow_graph() const { return flow_graph_; }
|
|
|
|
// Use ICData to optimize, replace or eliminate instructions.
|
|
void ApplyICData();
|
|
|
|
// Use propagated class ids to optimize, replace or eliminate instructions.
|
|
void ApplyClassIds();
|
|
|
|
// Optimize (a << b) & c pattern: if c is a positive Smi or zero, then the
|
|
// shift can be a truncating Smi shift-left and result is always Smi.
|
|
void TryOptimizeLeftShiftWithBitAndPattern();
|
|
|
|
// Returns true if any instructions were canonicalized away.
|
|
bool Canonicalize();
|
|
|
|
void EliminateDeadPhis();
|
|
|
|
void SelectRepresentations();
|
|
|
|
void InferSmiRanges();
|
|
|
|
void AnalyzeTryCatch();
|
|
|
|
// Remove environments from the instructions which do not deoptimize.
|
|
void EliminateEnvironments();
|
|
|
|
virtual void VisitStaticCall(StaticCallInstr* instr);
|
|
virtual void VisitInstanceCall(InstanceCallInstr* instr);
|
|
virtual void VisitRelationalOp(RelationalOpInstr* instr);
|
|
virtual void VisitEqualityCompare(EqualityCompareInstr* instr);
|
|
virtual void VisitBranch(BranchInstr* instr);
|
|
virtual void VisitStrictCompare(StrictCompareInstr* instr);
|
|
|
|
void InsertBefore(Instruction* next,
|
|
Instruction* instr,
|
|
Environment* env,
|
|
Definition::UseKind use_kind) {
|
|
flow_graph_->InsertBefore(next, instr, env, use_kind);
|
|
}
|
|
|
|
private:
|
|
// Attempt to build ICData for call using propagated class-ids.
|
|
bool TryCreateICData(InstanceCallInstr* call);
|
|
|
|
void SpecializePolymorphicInstanceCall(PolymorphicInstanceCallInstr* call);
|
|
|
|
intptr_t PrepareIndexedOp(InstanceCallInstr* call,
|
|
intptr_t class_id,
|
|
Definition** array,
|
|
Definition** index);
|
|
bool TryReplaceWithStoreIndexed(InstanceCallInstr* call);
|
|
void BuildStoreIndexed(InstanceCallInstr* call,
|
|
const ICData& value_check,
|
|
intptr_t class_id);
|
|
bool TryReplaceWithLoadIndexed(InstanceCallInstr* call);
|
|
|
|
bool TryReplaceWithBinaryOp(InstanceCallInstr* call, Token::Kind op_kind);
|
|
bool TryReplaceWithUnaryOp(InstanceCallInstr* call, Token::Kind op_kind);
|
|
|
|
bool TryInlineInstanceGetter(InstanceCallInstr* call);
|
|
bool TryInlineInstanceSetter(InstanceCallInstr* call,
|
|
const ICData& unary_ic_data);
|
|
|
|
bool TryInlineInstanceMethod(InstanceCallInstr* call);
|
|
bool TryInlineFloat32x4Constructor(StaticCallInstr* call,
|
|
MethodRecognizer::Kind recognized_kind);
|
|
bool TryInlineUint32x4Constructor(StaticCallInstr* call,
|
|
MethodRecognizer::Kind recognized_kind);
|
|
bool TryInlineFloat32x4Method(InstanceCallInstr* call,
|
|
MethodRecognizer::Kind recognized_kind);
|
|
bool TryInlineUint32x4Method(InstanceCallInstr* call,
|
|
MethodRecognizer::Kind recognized_kind);
|
|
void ReplaceWithInstanceOf(InstanceCallInstr* instr);
|
|
void ReplaceWithTypeCast(InstanceCallInstr* instr);
|
|
|
|
LoadIndexedInstr* BuildStringCodeUnitAt(InstanceCallInstr* call,
|
|
intptr_t cid);
|
|
|
|
bool BuildByteArrayViewLoad(InstanceCallInstr* call,
|
|
intptr_t receiver_cid,
|
|
intptr_t view_cid);
|
|
bool BuildByteArrayViewStore(InstanceCallInstr* call,
|
|
intptr_t view_cid);
|
|
void PrepareByteArrayViewOp(InstanceCallInstr* call,
|
|
intptr_t receiver_cid,
|
|
intptr_t view_cid,
|
|
Definition** array);
|
|
|
|
// Insert a check of 'to_check' determined by 'unary_checks'. If the
|
|
// check fails it will deoptimize to 'deopt_id' using the deoptimization
|
|
// environment 'deopt_environment'. The check is inserted immediately
|
|
// before 'insert_before'.
|
|
void AddCheckClass(Definition* to_check,
|
|
const ICData& unary_checks,
|
|
intptr_t deopt_id,
|
|
Environment* deopt_environment,
|
|
Instruction* insert_before);
|
|
|
|
// Insert a Smi check if needed.
|
|
void AddCheckSmi(Definition* to_check,
|
|
intptr_t deopt_id,
|
|
Environment* deopt_environment,
|
|
Instruction* insert_before);
|
|
|
|
// Add a class check for a call's first argument immediately before the
|
|
// call, using the call's IC data to determine the check, and the call's
|
|
// deopt ID and deoptimization environment if the check fails.
|
|
void AddReceiverCheck(InstanceCallInstr* call);
|
|
|
|
void ReplaceCall(Definition* call, Definition* replacement);
|
|
|
|
void InsertConversionsFor(Definition* def);
|
|
|
|
void ConvertUse(Value* use, Representation from);
|
|
|
|
void InsertConversion(Representation from,
|
|
Representation to,
|
|
Value* use,
|
|
Instruction* insert_before,
|
|
Instruction* deopt_target);
|
|
|
|
bool InstanceCallNeedsClassCheck(InstanceCallInstr* call) const;
|
|
bool MethodExtractorNeedsClassCheck(InstanceCallInstr* call) const;
|
|
|
|
bool InlineFloat32x4Getter(InstanceCallInstr* call,
|
|
MethodRecognizer::Kind getter);
|
|
bool InlineUint32x4Getter(InstanceCallInstr* call,
|
|
MethodRecognizer::Kind getter);
|
|
bool InlineFloat32x4BinaryOp(InstanceCallInstr* call,
|
|
Token::Kind op_kind);
|
|
bool InlineUint32x4BinaryOp(InstanceCallInstr* call,
|
|
Token::Kind op_kind);
|
|
void InlineImplicitInstanceGetter(InstanceCallInstr* call);
|
|
void InlineGrowableArrayCapacityGetter(InstanceCallInstr* call);
|
|
void InlineStringIsEmptyGetter(InstanceCallInstr* call);
|
|
void InlineObjectCid(InstanceCallInstr* call);
|
|
|
|
RawBool* InstanceOfAsBool(const ICData& ic_data,
|
|
const AbstractType& type) const;
|
|
|
|
void ReplaceWithMathCFunction(InstanceCallInstr* call,
|
|
MethodRecognizer::Kind recognized_kind);
|
|
|
|
void HandleComparison(ComparisonInstr* comp,
|
|
const ICData& ic_data,
|
|
Instruction* current_instruction);
|
|
|
|
void HandleRelationalOp(RelationalOpInstr* comp);
|
|
|
|
// Visit an equality compare. The current instruction can be the
|
|
// comparison itself or a branch on the comparison.
|
|
template <typename T>
|
|
void HandleEqualityCompare(EqualityCompareInstr* comp,
|
|
T current_instruction);
|
|
|
|
static bool CanStrictifyEqualityCompare(EqualityCompareInstr* call);
|
|
|
|
template <typename T>
|
|
bool StrictifyEqualityCompare(EqualityCompareInstr* compare,
|
|
T current_instruction) const;
|
|
|
|
template <typename T>
|
|
bool StrictifyEqualityCompareWithICData(EqualityCompareInstr* compare,
|
|
const ICData& unary_ic_data,
|
|
T current_instruction);
|
|
|
|
void OptimizeLeftShiftBitAndSmiOp(Definition* bit_and_instr,
|
|
Definition* left_instr,
|
|
Definition* right_instr);
|
|
|
|
void AddToGuardedFields(const Field& field);
|
|
|
|
FlowGraph* flow_graph_;
|
|
GrowableArray<const Field*>* guarded_fields_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(FlowGraphOptimizer);
|
|
};
|
|
|
|
|
|
// Loop invariant code motion.
|
|
class LICM : public ValueObject {
|
|
public:
|
|
explicit LICM(FlowGraph* flow_graph);
|
|
|
|
void Optimize();
|
|
|
|
private:
|
|
FlowGraph* flow_graph() const { return flow_graph_; }
|
|
|
|
void Hoist(ForwardInstructionIterator* it,
|
|
BlockEntryInstr* pre_header,
|
|
Instruction* current);
|
|
|
|
void TryHoistCheckSmiThroughPhi(ForwardInstructionIterator* it,
|
|
BlockEntryInstr* header,
|
|
BlockEntryInstr* pre_header,
|
|
CheckSmiInstr* current);
|
|
|
|
FlowGraph* const flow_graph_;
|
|
};
|
|
|
|
|
|
// A simple common subexpression elimination based
|
|
// on the dominator tree.
|
|
class DominatorBasedCSE : public AllStatic {
|
|
public:
|
|
// Return true, if the optimization changed the flow graph.
|
|
// False, if nothing changed.
|
|
static bool Optimize(FlowGraph* graph);
|
|
|
|
private:
|
|
static bool OptimizeRecursive(
|
|
FlowGraph* graph,
|
|
BlockEntryInstr* entry,
|
|
CSEInstructionMap* map);
|
|
};
|
|
|
|
|
|
// Sparse conditional constant propagation and unreachable code elimination.
|
|
// Assumes that use lists are computed and preserves them.
|
|
class ConstantPropagator : public FlowGraphVisitor {
|
|
public:
|
|
ConstantPropagator(FlowGraph* graph,
|
|
const GrowableArray<BlockEntryInstr*>& ignored);
|
|
|
|
static void Optimize(FlowGraph* graph);
|
|
|
|
// Only visit branches to optimize away unreachable blocks discovered
|
|
// by range analysis.
|
|
static void OptimizeBranches(FlowGraph* graph);
|
|
|
|
// Used to initialize the abstract value of definitions.
|
|
static RawObject* Unknown() { return Object::unknown_constant().raw(); }
|
|
|
|
private:
|
|
void Analyze();
|
|
void VisitBranches();
|
|
void Transform();
|
|
|
|
void SetReachable(BlockEntryInstr* block);
|
|
void SetValue(Definition* definition, const Object& value);
|
|
|
|
// Assign the join (least upper bound) of a pair of abstract values to the
|
|
// first one.
|
|
void Join(Object* left, const Object& right);
|
|
|
|
bool IsUnknown(const Object& value) {
|
|
return value.raw() == unknown_.raw();
|
|
}
|
|
bool IsNonConstant(const Object& value) {
|
|
return value.raw() == non_constant_.raw();
|
|
}
|
|
bool IsConstant(const Object& value) {
|
|
return !IsNonConstant(value) && !IsUnknown(value);
|
|
}
|
|
|
|
void HandleBinaryOp(Definition* instr,
|
|
Token::Kind op_kind,
|
|
const Value& left,
|
|
const Value& right);
|
|
|
|
virtual void VisitBlocks() { UNREACHABLE(); }
|
|
|
|
#define DECLARE_VISIT(type) virtual void Visit##type(type##Instr* instr);
|
|
FOR_EACH_INSTRUCTION(DECLARE_VISIT)
|
|
#undef DECLARE_VISIT
|
|
|
|
FlowGraph* graph_;
|
|
|
|
// Sentinels for unknown constant and non-constant values.
|
|
const Object& unknown_;
|
|
const Object& non_constant_;
|
|
|
|
// Analysis results. For each block, a reachability bit. Indexed by
|
|
// preorder number.
|
|
BitVector* reachable_;
|
|
|
|
// Definitions can move up the lattice twice, so we use a mark bit to
|
|
// indicate that they are already on the worklist in order to avoid adding
|
|
// them again. Indexed by SSA temp index.
|
|
BitVector* definition_marks_;
|
|
|
|
// Worklists of blocks and definitions.
|
|
GrowableArray<BlockEntryInstr*> block_worklist_;
|
|
GrowableArray<Definition*> definition_worklist_;
|
|
};
|
|
|
|
|
|
// Rewrite branches to eliminate materialization of boolean values after
|
|
// inlining, and to expose other optimizations (e.g., constant folding of
|
|
// branches, unreachable code elimination).
|
|
class BranchSimplifier : public AllStatic {
|
|
public:
|
|
static void Simplify(FlowGraph* flow_graph);
|
|
|
|
// Replace a target entry instruction with a join entry instruction. Does
|
|
// not update the original target's predecessors to point to the new block
|
|
// and does not replace the target in already computed block order lists.
|
|
static JoinEntryInstr* ToJoinEntry(TargetEntryInstr* target);
|
|
|
|
private:
|
|
// Match an instance of the pattern to rewrite. See the implementation
|
|
// for the patterns that are handled by this pass.
|
|
static bool Match(JoinEntryInstr* block);
|
|
|
|
// Duplicate a branch while replacing its comparison's left and right
|
|
// inputs.
|
|
static BranchInstr* CloneBranch(BranchInstr* branch,
|
|
Value* left,
|
|
Value* right);
|
|
};
|
|
|
|
|
|
// Rewrite diamond control flow patterns that materialize values to use more
|
|
// efficient branchless code patterns if such are supported on the current
|
|
// platform.
|
|
class IfConverter : public AllStatic {
|
|
public:
|
|
static void Simplify(FlowGraph* flow_graph);
|
|
};
|
|
|
|
|
|
class AllocationSinking : public ZoneAllocated {
|
|
public:
|
|
explicit AllocationSinking(FlowGraph* flow_graph)
|
|
: flow_graph_(flow_graph),
|
|
materializations_(5) { }
|
|
|
|
void Optimize();
|
|
|
|
void DetachMaterializations();
|
|
|
|
private:
|
|
void InsertMaterializations(AllocateObjectInstr* alloc);
|
|
|
|
void CreateMaterializationAt(
|
|
Instruction* exit,
|
|
AllocateObjectInstr* alloc,
|
|
const Class& cls,
|
|
const ZoneGrowableArray<const Field*>& fields);
|
|
|
|
FlowGraph* flow_graph_;
|
|
|
|
GrowableArray<MaterializeObjectInstr*> materializations_;
|
|
};
|
|
|
|
|
|
// Optimize spill stores inside try-blocks by identifying values that always
|
|
// contain a single known constant at catch block entry.
|
|
class TryCatchAnalyzer : public AllStatic {
|
|
public:
|
|
static void Optimize(FlowGraph* flow_graph);
|
|
};
|
|
|
|
} // namespace dart
|
|
|
|
#endif // VM_FLOW_GRAPH_OPTIMIZER_H_
|