8a179fb953
New folder structure (nested under vm/): - compiler/ - jit/ - JIT specific code - aot/ - AOT specific code - backend/ - all middle-end and back-end code (IL, flow graph) - assembler/ - assemblers and disassemblers - frontend/ - front ends (AST -> IL, Kernel -> IL) compiler/README.md would be the documentation root for the compiler pipeline Bug: https://github.com/dart-lang/sdk/issues/30575 Change-Id: I2dfd9688793bff737f7632ddc77fca766875ce36 Reviewed-on: https://dart-review.googlesource.com/2940 Reviewed-by: Vyacheslav Egorov <vegorov@google.com> Commit-Queue: Vyacheslav Egorov <vegorov@google.com>
531 lines
18 KiB
C++
531 lines
18 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 RUNTIME_VM_COMPILER_BACKEND_FLOW_GRAPH_H_
|
|
#define RUNTIME_VM_COMPILER_BACKEND_FLOW_GRAPH_H_
|
|
|
|
#include "vm/bit_vector.h"
|
|
#include "vm/compiler/backend/il.h"
|
|
#include "vm/growable_array.h"
|
|
#include "vm/hash_map.h"
|
|
#include "vm/parser.h"
|
|
#include "vm/thread.h"
|
|
|
|
namespace dart {
|
|
|
|
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());
|
|
}
|
|
|
|
void EnsureSSATempIndex(Definition* defn, Definition* replacement);
|
|
|
|
void ReplaceCurrentInstruction(ForwardInstructionIterator* iterator,
|
|
Instruction* current,
|
|
Instruction* replacement);
|
|
|
|
Instruction* CreateCheckClass(Definition* to_check,
|
|
const Cids& cids,
|
|
intptr_t deopt_id,
|
|
TokenPosition token_pos);
|
|
|
|
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();
|
|
}
|
|
|
|
bool InstanceCallNeedsClassCheck(InstanceCallInstr* call,
|
|
RawFunction::Kind kind) const;
|
|
|
|
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();
|
|
bool VerifyRedefinitions();
|
|
|
|
void DiscoverBlocks();
|
|
|
|
void MergeBlocks();
|
|
|
|
// Insert a redefinition of an original definition after prev and rename all
|
|
// dominated uses of the original. If an equivalent redefinition is already
|
|
// present, nothing is inserted.
|
|
// Returns the redefinition, if a redefinition was inserted, NULL otherwise.
|
|
RedefinitionInstr* EnsureRedefinition(Instruction* prev,
|
|
Definition* original,
|
|
CompileType compile_type);
|
|
|
|
// 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(); }
|
|
|
|
void AddToDeferredPrefixes(ZoneGrowableArray<const LibraryPrefix*>* from);
|
|
|
|
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; }
|
|
|
|
// Returns true if any instructions were canonicalized away.
|
|
bool Canonicalize();
|
|
|
|
void SelectRepresentations();
|
|
|
|
void WidenSmiToInt32();
|
|
|
|
// Remove environments from the instructions which do not deoptimize.
|
|
void EliminateEnvironments();
|
|
|
|
bool IsReceiver(Definition* def) const;
|
|
|
|
// 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.
|
|
// Merge instructions (only per basic-block).
|
|
void TryOptimizePatterns();
|
|
|
|
ZoneGrowableArray<TokenPosition>* await_token_positions() const {
|
|
return await_token_positions_;
|
|
}
|
|
|
|
void set_await_token_positions(
|
|
ZoneGrowableArray<TokenPosition>* await_token_positions) {
|
|
await_token_positions_ = await_token_positions;
|
|
}
|
|
|
|
// Replaces uses that are dominated by dom of 'def' with 'other'.
|
|
// Note: uses that occur at instruction dom itself are not dominated by it.
|
|
static void RenameDominatedUses(Definition* def,
|
|
Instruction* dom,
|
|
Definition* other);
|
|
|
|
// Renames uses of redefined values to make sure that uses of redefined
|
|
// values that are dominated by a redefinition are renamed.
|
|
void RenameUsesDominatedByRedefinitions();
|
|
|
|
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;
|
|
|
|
void InsertConversionsFor(Definition* def);
|
|
void ConvertUse(Value* use, Representation from);
|
|
void ConvertEnvironmentUse(Value* use, Representation from);
|
|
void InsertConversion(Representation from,
|
|
Representation to,
|
|
Value* use,
|
|
bool is_environment_use);
|
|
|
|
void ComputeIsReceiver(PhiInstr* phi) const;
|
|
void ComputeIsReceiverRecursive(PhiInstr* phi,
|
|
GrowableArray<PhiInstr*>* unmark) const;
|
|
|
|
void OptimizeLeftShiftBitAndSmiOp(
|
|
ForwardInstructionIterator* current_iterator,
|
|
Definition* bit_and_instr,
|
|
Definition* left_instr,
|
|
Definition* right_instr);
|
|
|
|
void TryMergeTruncDivMod(GrowableArray<BinarySmiOpInstr*>* merge_candidates);
|
|
|
|
void AppendExtractNthOutputForMerged(Definition* instr,
|
|
intptr_t ix,
|
|
Representation rep,
|
|
intptr_t cid);
|
|
|
|
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_;
|
|
|
|
bool licm_allowed_;
|
|
|
|
ZoneGrowableArray<BlockEntryInstr*>* loop_headers_;
|
|
ZoneGrowableArray<BitVector*>* loop_invariant_loads_;
|
|
ZoneGrowableArray<const LibraryPrefix*>* deferred_prefixes_;
|
|
ZoneGrowableArray<TokenPosition>* await_token_positions_;
|
|
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_;
|
|
};
|
|
|
|
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 // RUNTIME_VM_COMPILER_BACKEND_FLOW_GRAPH_H_
|