blob: 215d26fe02930b4b604f5f0c5cc7e3e0fc9629c8 [file] [log] [blame]
// 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_BACKEND_LOOPS_H_
#define RUNTIME_VM_COMPILER_BACKEND_LOOPS_H_
#include "vm/allocation.h"
#include "vm/compiler/backend/il.h"
namespace dart {
// Information on an induction variable in a particular loop.
//
// Invariant:
// offset + mult * def
// Linear:
// initial + next * i, for invariant initial and next,
// and a "normalized" loop index i
// Wrap-around:
// initial then next, for invariant initial and any next
// Periodic:
// alternate initial and next, for invariant initial and next
//
class InductionVar : public ZoneAllocated {
public:
enum Kind {
kInvariant,
kLinear,
kWrapAround,
kPeriodic,
};
// Strict bound on unit stride linear induction:
// i < U (i++)
// i > L (i--)
struct Bound {
Bound(BranchInstr* b, InductionVar* l) : branch_(b), limit_(l) {}
BranchInstr* branch_;
InductionVar* limit_;
};
// Constructor for an invariant.
InductionVar(int64_t offset, int64_t mult, Definition* def)
: kind_(kInvariant), offset_(offset), mult_(mult), def_(def) {}
// Constructor for a constant.
explicit InductionVar(int64_t offset) : InductionVar(offset, 0, nullptr) {}
// Constructor for an induction.
InductionVar(Kind kind, InductionVar* initial, InductionVar* next)
: kind_(kind), initial_(initial), next_(next) {
ASSERT(IsInvariant(initial));
switch (kind) {
case kLinear:
case kPeriodic:
ASSERT(IsInvariant(next));
break;
case kWrapAround:
ASSERT(next != nullptr);
break;
default:
UNREACHABLE();
}
}
// Returns true if the other induction is structually equivalent.
bool IsEqual(InductionVar* other) const {
ASSERT(other != nullptr);
if (kind_ == other->kind_) {
switch (kind_) {
case kInvariant:
return offset_ == other->offset_ && mult_ == other->mult_ &&
(mult_ == 0 || def_ == other->def_);
case kLinear:
case kWrapAround:
case kPeriodic:
return initial_->IsEqual(other->initial_) &&
next_->IsEqual(other->next_);
}
}
return false;
}
// Getters.
Kind kind() const { return kind_; }
int64_t offset() const {
ASSERT(kind_ == kInvariant);
return offset_;
}
int64_t mult() const {
ASSERT(kind_ == kInvariant);
return mult_;
}
Definition* def() const {
ASSERT(kind_ == kInvariant);
return def_;
}
InductionVar* initial() const {
ASSERT(kind_ != kInvariant);
return initial_;
}
InductionVar* next() const {
ASSERT(kind_ != kInvariant);
return next_;
}
const GrowableArray<Bound>& bounds() { return bounds_; }
// For debugging.
const char* ToCString() const;
// Returns true if x is invariant.
static bool IsInvariant(InductionVar* x) {
return x != nullptr && x->kind_ == kInvariant;
}
// Returns true if x is a constant (and invariant).
static bool IsConstant(InductionVar* x) {
return x != nullptr && x->kind_ == kInvariant && x->mult_ == 0;
}
// Returns true if x is a constant. Sets the value.
static bool IsConstant(InductionVar* x, int64_t* c) {
if (IsConstant(x)) {
*c = x->offset_;
return true;
}
return false;
}
// Returns true if x is linear.
static bool IsLinear(InductionVar* x) {
return x != nullptr && x->kind_ == kLinear;
}
// Returns true if x is linear with constant stride. Sets the stride.
static bool IsLinear(InductionVar* x, int64_t* s) {
if (IsLinear(x)) {
return IsConstant(x->next_, s);
}
return false;
}
// Returns true if x is wrap-around.
static bool IsWrapAround(InductionVar* x) {
return x != nullptr && x->kind_ == kWrapAround;
}
// Returns true if x is periodic.
static bool IsPeriodic(InductionVar* x) {
return x != nullptr && x->kind_ == kPeriodic;
}
// Returns true if x is any induction.
static bool IsInduction(InductionVar* x) {
return x != nullptr && x->kind_ != kInvariant;
}
private:
friend class InductionVarAnalysis;
// Induction classification.
const Kind kind_;
union {
struct {
int64_t offset_;
int64_t mult_;
Definition* def_;
};
struct {
InductionVar* initial_;
InductionVar* next_;
};
};
// Bounds on induction.
GrowableArray<Bound> bounds_;
DISALLOW_COPY_AND_ASSIGN(InductionVar);
};
// Information on a "natural loop" in the flow graph.
class LoopInfo : public ZoneAllocated {
public:
LoopInfo(intptr_t id, BlockEntryInstr* header, BitVector* blocks);
// Merges given blocks to this loop.
void AddBlocks(BitVector* blocks);
// Adds back edge to this loop.
void AddBackEdge(BlockEntryInstr* block);
// Returns true if given block is backedge of this loop.
bool IsBackEdge(BlockEntryInstr* block) const;
// Returns true if given block is alway taken in this loop.
bool IsAlwaysTaken(BlockEntryInstr* block) const;
// Returns true if given definition is a header phi for this loop.
bool IsHeaderPhi(Definition* def) const;
// Returns true if this loop is nested inside given loop.
bool IsIn(LoopInfo* loop) const;
// Returns true if this loop contains given block.
bool Contains(BlockEntryInstr* block) const;
// Returns the nesting depth of this loop.
intptr_t NestingDepth() const;
// Resets induction.
void ResetInduction();
// Assigns induction to a definition.
void AddInduction(Definition* def, InductionVar* induc);
// Looks up induction.
InductionVar* LookupInduction(Definition* def) const;
// Getters.
intptr_t id() const { return id_; }
BlockEntryInstr* header() const { return header_; }
BitVector* blocks() const { return blocks_; }
const GrowableArray<BlockEntryInstr*>& back_edges() { return back_edges_; }
ConstraintInstr* limit() const { return limit_; }
InductionVar* control() const { return control_; }
LoopInfo* outer() const { return outer_; }
LoopInfo* inner() const { return inner_; }
LoopInfo* next() const { return next_; }
// For debugging.
const char* ToCString() const;
private:
friend class InductionVarAnalysis;
friend class LoopHierarchy;
typedef RawPointerKeyValueTrait<Definition, InductionVar*> InductionKV;
// Unique id of loop. We use its index in the
// loop header array for this.
const intptr_t id_;
// Header of loop.
BlockEntryInstr* header_;
// Compact represention of every block in the loop,
// indexed by its "preorder_number".
BitVector* blocks_;
// Back edges of loop (usually one).
GrowableArray<BlockEntryInstr*> back_edges_;
// Map definition -> induction for this loop.
DirectChainedHashMap<InductionKV> induction_;
// Constraint on a header phi.
// TODO(ajcbik): very specific to smi range analysis,
// should we really store it here?
ConstraintInstr* limit_;
// Control induction.
InductionVar* control_;
// Loop hierarchy.
LoopInfo* outer_;
LoopInfo* inner_;
LoopInfo* next_;
DISALLOW_COPY_AND_ASSIGN(LoopInfo);
};
// Information on the loop hierarchy in the flow graph.
class LoopHierarchy : public ZoneAllocated {
public:
LoopHierarchy(ZoneGrowableArray<BlockEntryInstr*>* headers,
const GrowableArray<BlockEntryInstr*>& preorder);
// Getters.
const ZoneGrowableArray<BlockEntryInstr*>& headers() const {
return *headers_;
}
LoopInfo* top() const { return top_; }
// Returns total number of loops in the hierarchy.
intptr_t num_loops() const { return headers_->length(); }
// Performs induction variable analysis on all loops.
void ComputeInduction() const;
private:
void Build();
void Print(LoopInfo* loop);
ZoneGrowableArray<BlockEntryInstr*>* headers_;
const GrowableArray<BlockEntryInstr*>& preorder_;
LoopInfo* top_;
DISALLOW_COPY_AND_ASSIGN(LoopHierarchy);
};
} // namespace dart
#endif // RUNTIME_VM_COMPILER_BACKEND_LOOPS_H_