9c181ec6d5
Refactor all remaning cases where the current zone is used through new(Isolate*) and remove this interface. Removing this interface is needed to move towards multiple threads per isolate, and also makes the caller more aware of the scope of the zone used, reducing the risk of use-after-free. Make the current thread and the stack zone created around native/runtime entries directly available in their body, saving an indirection (and optimized away if unused). R=iposva@google.com Review URL: https://codereview.chromium.org//982873004 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@44541 260f80e4-7a28-3924-810f-c04153c831b5
526 lines
16 KiB
C++
526 lines
16 KiB
C++
// Copyright (c) 2013, 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_H_
|
|
#define VM_FLOW_GRAPH_H_
|
|
|
|
#include "vm/bit_vector.h"
|
|
#include "vm/growable_array.h"
|
|
#include "vm/hash_map.h"
|
|
#include "vm/intermediate_language.h"
|
|
#include "vm/parser.h"
|
|
#include "vm/thread.h"
|
|
|
|
namespace dart {
|
|
|
|
class BlockEffects;
|
|
class FlowGraphBuilder;
|
|
class ValueInliningContext;
|
|
class VariableLivenessAnalysis;
|
|
|
|
class BlockIterator : public ValueObject {
|
|
public:
|
|
explicit BlockIterator(const GrowableArray<BlockEntryInstr*>& block_order)
|
|
: block_order_(block_order), current_(0) { }
|
|
|
|
BlockIterator(const BlockIterator& other)
|
|
: ValueObject(),
|
|
block_order_(other.block_order_),
|
|
current_(other.current_) { }
|
|
|
|
void Advance() {
|
|
ASSERT(!Done());
|
|
current_++;
|
|
}
|
|
|
|
bool Done() const { return current_ >= block_order_.length(); }
|
|
|
|
BlockEntryInstr* Current() const { return block_order_[current_]; }
|
|
|
|
private:
|
|
const GrowableArray<BlockEntryInstr*>& block_order_;
|
|
intptr_t current_;
|
|
};
|
|
|
|
|
|
struct ConstantPoolTrait {
|
|
typedef ConstantInstr* Value;
|
|
typedef const Object& Key;
|
|
typedef ConstantInstr* Pair;
|
|
|
|
static Key KeyOf(Pair kv) {
|
|
return kv->value();
|
|
}
|
|
|
|
static Value ValueOf(Pair kv) {
|
|
return kv;
|
|
}
|
|
|
|
static inline intptr_t Hashcode(Key key) {
|
|
if (key.IsSmi()) {
|
|
return Smi::Cast(key).Value();
|
|
}
|
|
if (key.IsDouble()) {
|
|
return static_cast<intptr_t>(
|
|
bit_cast<int32_t, float>(
|
|
static_cast<float>(Double::Cast(key).value())));
|
|
}
|
|
if (key.IsMint()) {
|
|
return static_cast<intptr_t>(Mint::Cast(key).value());
|
|
}
|
|
if (key.IsString()) {
|
|
return String::Cast(key).Hash();
|
|
}
|
|
return key.GetClassId();
|
|
}
|
|
|
|
static inline bool IsKeyEqual(Pair kv, Key key) {
|
|
return kv->value().raw() == key.raw();
|
|
}
|
|
};
|
|
|
|
|
|
// Class to encapsulate the construction and manipulation of the flow graph.
|
|
class FlowGraph : public ZoneAllocated {
|
|
public:
|
|
FlowGraph(const ParsedFunction& parsed_function,
|
|
GraphEntryInstr* graph_entry,
|
|
intptr_t max_block_id);
|
|
|
|
// Function properties.
|
|
const ParsedFunction& parsed_function() const {
|
|
return parsed_function_;
|
|
}
|
|
const Function& function() const {
|
|
return parsed_function_.function();
|
|
}
|
|
intptr_t parameter_count() const {
|
|
return num_copied_params_ + num_non_copied_params_;
|
|
}
|
|
intptr_t variable_count() const {
|
|
return parameter_count() + parsed_function_.num_stack_locals();
|
|
}
|
|
intptr_t num_stack_locals() const {
|
|
return parsed_function_.num_stack_locals();
|
|
}
|
|
intptr_t num_copied_params() const {
|
|
return num_copied_params_;
|
|
}
|
|
intptr_t num_non_copied_params() const {
|
|
return num_non_copied_params_;
|
|
}
|
|
bool IsIrregexpFunction() const {
|
|
return function().IsIrregexpFunction();
|
|
}
|
|
|
|
LocalVariable* CurrentContextVar() const {
|
|
return parsed_function().current_context_var();
|
|
}
|
|
|
|
intptr_t CurrentContextEnvIndex() const {
|
|
return parsed_function().current_context_var()->BitIndexIn(
|
|
num_non_copied_params_);
|
|
}
|
|
|
|
// Flow graph orders.
|
|
const GrowableArray<BlockEntryInstr*>& preorder() const {
|
|
return preorder_;
|
|
}
|
|
const GrowableArray<BlockEntryInstr*>& postorder() const {
|
|
return postorder_;
|
|
}
|
|
const GrowableArray<BlockEntryInstr*>& reverse_postorder() const {
|
|
return reverse_postorder_;
|
|
}
|
|
static bool ShouldReorderBlocks(const Function& function, bool is_optimized);
|
|
GrowableArray<BlockEntryInstr*>* CodegenBlockOrder(bool is_optimized);
|
|
|
|
// Iterators.
|
|
BlockIterator reverse_postorder_iterator() const {
|
|
return BlockIterator(reverse_postorder());
|
|
}
|
|
BlockIterator postorder_iterator() const {
|
|
return BlockIterator(postorder());
|
|
}
|
|
|
|
intptr_t current_ssa_temp_index() const { return current_ssa_temp_index_; }
|
|
void set_current_ssa_temp_index(intptr_t index) {
|
|
current_ssa_temp_index_ = index;
|
|
}
|
|
|
|
intptr_t max_virtual_register_number() const {
|
|
return current_ssa_temp_index();
|
|
}
|
|
|
|
Thread* thread() const { return thread_; }
|
|
Zone* zone() const { return thread()->zone(); }
|
|
Isolate* isolate() const { return thread()->isolate(); }
|
|
|
|
intptr_t max_block_id() const { return max_block_id_; }
|
|
void set_max_block_id(intptr_t id) { max_block_id_ = id; }
|
|
intptr_t allocate_block_id() { return ++max_block_id_; }
|
|
|
|
GraphEntryInstr* graph_entry() const {
|
|
return graph_entry_;
|
|
}
|
|
|
|
ConstantInstr* constant_null() const {
|
|
return constant_null_;
|
|
}
|
|
|
|
ConstantInstr* constant_dead() const {
|
|
return constant_dead_;
|
|
}
|
|
|
|
ConstantInstr* constant_empty_context() const {
|
|
return constant_empty_context_;
|
|
}
|
|
|
|
intptr_t alloc_ssa_temp_index() { return current_ssa_temp_index_++; }
|
|
|
|
void AllocateSSAIndexes(Definition* def) {
|
|
ASSERT(def);
|
|
def->set_ssa_temp_index(alloc_ssa_temp_index());
|
|
// Always allocate a second index. This index is unused except
|
|
// for Definitions with register pair outputs.
|
|
alloc_ssa_temp_index();
|
|
}
|
|
|
|
intptr_t InstructionCount() const;
|
|
|
|
ConstantInstr* GetConstant(const Object& object);
|
|
void AddToInitialDefinitions(Definition* defn);
|
|
|
|
enum UseKind { kEffect, kValue };
|
|
|
|
void InsertBefore(Instruction* next,
|
|
Instruction* instr,
|
|
Environment* env,
|
|
UseKind use_kind);
|
|
void InsertAfter(Instruction* prev,
|
|
Instruction* instr,
|
|
Environment* env,
|
|
UseKind use_kind);
|
|
Instruction* AppendTo(Instruction* prev,
|
|
Instruction* instr,
|
|
Environment* env,
|
|
UseKind use_kind);
|
|
|
|
// Operations on the flow graph.
|
|
void ComputeSSA(intptr_t next_virtual_register_number,
|
|
ZoneGrowableArray<Definition*>* inlining_parameters);
|
|
|
|
// Verification methods for debugging.
|
|
bool VerifyUseLists();
|
|
|
|
void DiscoverBlocks();
|
|
|
|
void MergeBlocks();
|
|
|
|
// Compute information about effects occuring in different blocks and
|
|
// discover side-effect free paths.
|
|
void ComputeBlockEffects();
|
|
BlockEffects* block_effects() const { return block_effects_; }
|
|
|
|
// Remove the redefinition instructions inserted to inhibit code motion.
|
|
void RemoveRedefinitions();
|
|
|
|
// Copy deoptimization target from one instruction to another if we still
|
|
// have to keep deoptimization environment at gotos for LICM purposes.
|
|
void CopyDeoptTarget(Instruction* to, Instruction* from) {
|
|
if (is_licm_allowed()) {
|
|
to->InheritDeoptTarget(zone(), from);
|
|
}
|
|
}
|
|
|
|
// Returns true if every Goto in the graph is expected to have a
|
|
// deoptimization environment and can be used as deoptimization target
|
|
// for hoisted instructions.
|
|
bool is_licm_allowed() const { return licm_allowed_; }
|
|
|
|
// Stop preserving environments on Goto instructions. LICM is not allowed
|
|
// after this point.
|
|
void disallow_licm() { licm_allowed_ = false; }
|
|
|
|
const ZoneGrowableArray<BlockEntryInstr*>& LoopHeaders() {
|
|
if (loop_headers_ == NULL) {
|
|
loop_headers_ = ComputeLoops();
|
|
}
|
|
return *loop_headers_;
|
|
}
|
|
|
|
const ZoneGrowableArray<BlockEntryInstr*>* loop_headers() const {
|
|
return loop_headers_;
|
|
}
|
|
|
|
// Finds natural loops in the flow graph and attaches a list of loop
|
|
// body blocks for each loop header.
|
|
ZoneGrowableArray<BlockEntryInstr*>* ComputeLoops() const;
|
|
|
|
// Per loop header invariant loads sets. Each set contains load id for
|
|
// those loads that are not affected by anything in the loop and can be
|
|
// hoisted out. Sets are computed by LoadOptimizer.
|
|
ZoneGrowableArray<BitVector*>* loop_invariant_loads() const {
|
|
return loop_invariant_loads_;
|
|
}
|
|
void set_loop_invariant_loads(
|
|
ZoneGrowableArray<BitVector*>* loop_invariant_loads) {
|
|
loop_invariant_loads_ = loop_invariant_loads;
|
|
}
|
|
|
|
bool IsCompiledForOsr() const { return graph_entry()->IsCompiledForOsr(); }
|
|
|
|
static void AddToGuardedFields(ZoneGrowableArray<const Field*>* array,
|
|
const Field* field);
|
|
void AddToDeferredPrefixes(ZoneGrowableArray<const LibraryPrefix*>* from);
|
|
|
|
ZoneGrowableArray<const Field*>* guarded_fields() const {
|
|
return guarded_fields_;
|
|
}
|
|
|
|
ZoneGrowableArray<const LibraryPrefix*>* deferred_prefixes() const {
|
|
return deferred_prefixes_;
|
|
}
|
|
|
|
BitVector* captured_parameters() const {
|
|
return captured_parameters_;
|
|
}
|
|
|
|
intptr_t inlining_id() const { return inlining_id_; }
|
|
void set_inlining_id(intptr_t value) { inlining_id_ = value; }
|
|
|
|
private:
|
|
friend class IfConverter;
|
|
friend class BranchSimplifier;
|
|
friend class ConstantPropagator;
|
|
friend class DeadCodeElimination;
|
|
|
|
// SSA transformation methods and fields.
|
|
void ComputeDominators(GrowableArray<BitVector*>* dominance_frontier);
|
|
|
|
void CompressPath(
|
|
intptr_t start_index,
|
|
intptr_t current_index,
|
|
GrowableArray<intptr_t>* parent,
|
|
GrowableArray<intptr_t>* label);
|
|
|
|
void Rename(GrowableArray<PhiInstr*>* live_phis,
|
|
VariableLivenessAnalysis* variable_liveness,
|
|
ZoneGrowableArray<Definition*>* inlining_parameters);
|
|
void RenameRecursive(
|
|
BlockEntryInstr* block_entry,
|
|
GrowableArray<Definition*>* env,
|
|
GrowableArray<PhiInstr*>* live_phis,
|
|
VariableLivenessAnalysis* variable_liveness);
|
|
|
|
void AttachEnvironment(Instruction* instr, GrowableArray<Definition*>* env);
|
|
|
|
void InsertPhis(
|
|
const GrowableArray<BlockEntryInstr*>& preorder,
|
|
const GrowableArray<BitVector*>& assigned_vars,
|
|
const GrowableArray<BitVector*>& dom_frontier,
|
|
GrowableArray<PhiInstr*>* live_phis);
|
|
|
|
void RemoveDeadPhis(GrowableArray<PhiInstr*>* live_phis);
|
|
|
|
void ReplacePredecessor(BlockEntryInstr* old_block,
|
|
BlockEntryInstr* new_block);
|
|
|
|
// Find the natural loop for the back edge m->n and attach loop
|
|
// information to block n (loop header). The algorithm is described in
|
|
// "Advanced Compiler Design & Implementation" (Muchnick) p192.
|
|
// Returns a BitVector indexed by block pre-order number where each bit
|
|
// indicates membership in the loop.
|
|
BitVector* FindLoop(BlockEntryInstr* m, BlockEntryInstr* n) const;
|
|
|
|
Thread* thread_;
|
|
|
|
// DiscoverBlocks computes parent_ and assigned_vars_ which are then used
|
|
// if/when computing SSA.
|
|
GrowableArray<intptr_t> parent_;
|
|
GrowableArray<BitVector*> assigned_vars_;
|
|
|
|
intptr_t current_ssa_temp_index_;
|
|
intptr_t max_block_id_;
|
|
|
|
// Flow graph fields.
|
|
const ParsedFunction& parsed_function_;
|
|
const intptr_t num_copied_params_;
|
|
const intptr_t num_non_copied_params_;
|
|
GraphEntryInstr* graph_entry_;
|
|
GrowableArray<BlockEntryInstr*> preorder_;
|
|
GrowableArray<BlockEntryInstr*> postorder_;
|
|
GrowableArray<BlockEntryInstr*> reverse_postorder_;
|
|
GrowableArray<BlockEntryInstr*> optimized_block_order_;
|
|
ConstantInstr* constant_null_;
|
|
ConstantInstr* constant_dead_;
|
|
ConstantInstr* constant_empty_context_;
|
|
|
|
BlockEffects* block_effects_;
|
|
bool licm_allowed_;
|
|
|
|
ZoneGrowableArray<BlockEntryInstr*>* loop_headers_;
|
|
ZoneGrowableArray<BitVector*>* loop_invariant_loads_;
|
|
ZoneGrowableArray<const Field*>* guarded_fields_;
|
|
ZoneGrowableArray<const LibraryPrefix*>* deferred_prefixes_;
|
|
DirectChainedHashMap<ConstantPoolTrait> constant_instr_pool_;
|
|
BitVector* captured_parameters_;
|
|
|
|
intptr_t inlining_id_;
|
|
};
|
|
|
|
|
|
class LivenessAnalysis : public ValueObject {
|
|
public:
|
|
LivenessAnalysis(intptr_t variable_count,
|
|
const GrowableArray<BlockEntryInstr*>& postorder);
|
|
|
|
void Analyze();
|
|
|
|
virtual ~LivenessAnalysis() { }
|
|
|
|
BitVector* GetLiveInSetAt(intptr_t postorder_number) const {
|
|
return live_in_[postorder_number];
|
|
}
|
|
|
|
BitVector* GetLiveOutSetAt(intptr_t postorder_number) const {
|
|
return live_out_[postorder_number];
|
|
}
|
|
|
|
BitVector* GetLiveInSet(BlockEntryInstr* block) const {
|
|
return GetLiveInSetAt(block->postorder_number());
|
|
}
|
|
|
|
BitVector* GetKillSet(BlockEntryInstr* block) const {
|
|
return kill_[block->postorder_number()];
|
|
}
|
|
|
|
BitVector* GetLiveOutSet(BlockEntryInstr* block) const {
|
|
return GetLiveOutSetAt(block->postorder_number());
|
|
}
|
|
|
|
// Print results of liveness analysis.
|
|
void Dump();
|
|
|
|
protected:
|
|
// Compute initial values for live-out, kill and live-in sets.
|
|
virtual void ComputeInitialSets() = 0;
|
|
|
|
// Update live-out set for the given block: live-out should contain
|
|
// all values that are live-in for block's successors.
|
|
// Returns true if live-out set was changed.
|
|
bool UpdateLiveOut(const BlockEntryInstr& instr);
|
|
|
|
// Update live-in set for the given block: live-in should contain
|
|
// all values that are live-out from the block and are not defined
|
|
// by this block.
|
|
// Returns true if live-in set was changed.
|
|
bool UpdateLiveIn(const BlockEntryInstr& instr);
|
|
|
|
// Perform fix-point iteration updating live-out and live-in sets
|
|
// for blocks until they stop changing.
|
|
void ComputeLiveInAndLiveOutSets();
|
|
|
|
Zone* zone() const { return zone_; }
|
|
|
|
Zone* zone_;
|
|
|
|
const intptr_t variable_count_;
|
|
|
|
const GrowableArray<BlockEntryInstr*>& postorder_;
|
|
|
|
// Live-out sets for each block. They contain indices of variables
|
|
// that are live out from this block: that is values that were either
|
|
// defined in this block or live into it and that are used in some
|
|
// successor block.
|
|
GrowableArray<BitVector*> live_out_;
|
|
|
|
// Kill sets for each block. They contain indices of variables that
|
|
// are defined by this block.
|
|
GrowableArray<BitVector*> kill_;
|
|
|
|
// Live-in sets for each block. They contain indices of variables
|
|
// that are used by this block or its successors.
|
|
GrowableArray<BitVector*> live_in_;
|
|
};
|
|
|
|
|
|
// Information about side effect free paths between blocks.
|
|
class BlockEffects : public ZoneAllocated {
|
|
public:
|
|
explicit BlockEffects(FlowGraph* flow_graph);
|
|
|
|
// Return true if the given instruction is not affected by anything between
|
|
// its current block and target block. Used by CSE to determine if
|
|
// a computation is available in the given block.
|
|
bool IsAvailableAt(Instruction* instr, BlockEntryInstr* block) const;
|
|
|
|
// Return true if the given instruction is not affected by anything between
|
|
// the given block and its current block. Used by LICM to determine if
|
|
// a computation can be moved to loop's preheader and remain available at
|
|
// its current location.
|
|
bool CanBeMovedTo(Instruction* instr, BlockEntryInstr* block) const;
|
|
|
|
private:
|
|
// Returns true if from dominates to and all paths between from and to are
|
|
// free of side effects.
|
|
bool IsSideEffectFreePath(BlockEntryInstr* from, BlockEntryInstr* to) const;
|
|
|
|
// Per block sets of available blocks. Block A is available at the block B if
|
|
// and only if A dominates B and all paths from A to B are free of side
|
|
// effects.
|
|
GrowableArray<BitVector*> available_at_;
|
|
};
|
|
|
|
|
|
class DefinitionWorklist : public ValueObject {
|
|
public:
|
|
DefinitionWorklist(FlowGraph* flow_graph,
|
|
intptr_t initial_capacity)
|
|
: defs_(initial_capacity),
|
|
contains_vector_(
|
|
new BitVector(flow_graph->zone(),
|
|
flow_graph->current_ssa_temp_index())) {
|
|
}
|
|
|
|
void Add(Definition* defn) {
|
|
if (!Contains(defn)) {
|
|
defs_.Add(defn);
|
|
contains_vector_->Add(defn->ssa_temp_index());
|
|
}
|
|
}
|
|
|
|
bool Contains(Definition* defn) const {
|
|
return (defn->ssa_temp_index() >= 0) &&
|
|
contains_vector_->Contains(defn->ssa_temp_index());
|
|
}
|
|
|
|
bool IsEmpty() const {
|
|
return defs_.is_empty();
|
|
}
|
|
|
|
Definition* RemoveLast() {
|
|
Definition* defn = defs_.RemoveLast();
|
|
contains_vector_->Remove(defn->ssa_temp_index());
|
|
return defn;
|
|
}
|
|
|
|
const GrowableArray<Definition*>& definitions() const { return defs_; }
|
|
BitVector* contains_vector() const { return contains_vector_; }
|
|
|
|
void Clear() {
|
|
defs_.TruncateTo(0);
|
|
contains_vector_->Clear();
|
|
}
|
|
|
|
private:
|
|
GrowableArray<Definition*> defs_;
|
|
BitVector* contains_vector_;
|
|
};
|
|
|
|
|
|
} // namespace dart
|
|
|
|
#endif // VM_FLOW_GRAPH_H_
|