|  | // 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_ | 
|  |  | 
|  | #if defined(DART_PRECOMPILED_RUNTIME) | 
|  | #error "AOT runtime should not use compiler sources (including header files)" | 
|  | #endif  // defined(DART_PRECOMPILED_RUNTIME) | 
|  |  | 
|  | #include <utility> | 
|  |  | 
|  | #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 (exclusive) upper or lower 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), bounds_() { | 
|  | ASSERT(mult_ == 0 || def != nullptr); | 
|  | } | 
|  |  | 
|  | // 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), bounds_() { | 
|  | 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 structurally equivalent. | 
|  | bool IsEqual(const 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; | 
|  | } | 
|  |  | 
|  | // Returns true if a fixed difference between this and the other induction | 
|  | // can be computed. Sets the output parameter diff on success. | 
|  | bool CanComputeDifferenceWith(const InductionVar* other, int64_t* diff) const; | 
|  |  | 
|  | // Returns true if this induction in the given loop can be bounded as | 
|  | // min <= this <= max by using bounds of more outer loops. On success | 
|  | // the output parameters min and max are set, which are always loop | 
|  | // invariant expressions inside the given loop. | 
|  | bool CanComputeBounds(LoopInfo* loop, | 
|  | Instruction* pos, | 
|  | InductionVar** min, | 
|  | InductionVar** max); | 
|  |  | 
|  | // 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. | 
|  | void PrintTo(BaseTextBuffer* f) const; | 
|  | const char* ToCString() const; | 
|  |  | 
|  | // Returns true if x is invariant. | 
|  | static bool IsInvariant(const InductionVar* x) { | 
|  | return x != nullptr && x->kind_ == kInvariant; | 
|  | } | 
|  |  | 
|  | // Returns true if x is a constant (and invariant). | 
|  | static bool IsConstant(const InductionVar* x) { | 
|  | return x != nullptr && x->kind_ == kInvariant && x->mult_ == 0; | 
|  | } | 
|  |  | 
|  | // Returns true if x is a constant. Sets the value. | 
|  | static bool IsConstant(const InductionVar* x, int64_t* c) { | 
|  | if (IsConstant(x)) { | 
|  | *c = x->offset_; | 
|  | return true; | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // Returns true if x is linear. | 
|  | static bool IsLinear(const InductionVar* x) { | 
|  | return x != nullptr && x->kind_ == kLinear; | 
|  | } | 
|  |  | 
|  | // Returns true if x is linear with constant stride. Sets the stride. | 
|  | static bool IsLinear(const 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(const InductionVar* x) { | 
|  | return x != nullptr && x->kind_ == kWrapAround; | 
|  | } | 
|  |  | 
|  | // Returns true if x is periodic. | 
|  | static bool IsPeriodic(const InductionVar* x) { | 
|  | return x != nullptr && x->kind_ == kPeriodic; | 
|  | } | 
|  |  | 
|  | // Returns true if x is any induction. | 
|  | static bool IsInduction(const 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_; | 
|  | }; | 
|  | }; | 
|  |  | 
|  | bool CanComputeBoundsImpl(LoopInfo* loop, | 
|  | Instruction* pos, | 
|  | InductionVar** min, | 
|  | InductionVar** max); | 
|  |  | 
|  | // 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; | 
|  |  | 
|  | // Tests if index stays in [0,length) range in this loop at given position. | 
|  | bool IsInRange(Instruction* pos, Value* index, Value* length); | 
|  |  | 
|  | // 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. | 
|  | void PrintTo(BaseTextBuffer* f) const; | 
|  | const char* ToCString() const; | 
|  |  | 
|  | private: | 
|  | friend class InductionVar; | 
|  | friend class InductionVarAnalysis; | 
|  | friend class LoopHierarchy; | 
|  |  | 
|  | // Mapping from definition to induction. | 
|  | typedef RawPointerKeyValueTrait<Definition, InductionVar*> InductionKV; | 
|  |  | 
|  | // Mapping from induction to mapping from instruction to induction pair. | 
|  | class MemoVal : public ZoneAllocated { | 
|  | public: | 
|  | typedef RawPointerKeyValueTrait<Instruction, | 
|  | std::pair<InductionVar*, InductionVar*>> | 
|  | PosKV; | 
|  | MemoVal() : memo_() {} | 
|  | DirectChainedHashMap<PosKV> memo_; | 
|  | }; | 
|  | typedef RawPointerKeyValueTrait<InductionVar, MemoVal*> MemoKV; | 
|  |  | 
|  | // 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 representation 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_; | 
|  |  | 
|  | // A small, per-loop memoization cache, to avoid costly | 
|  | // recomputations while traversing very deeply nested loops. | 
|  | DirectChainedHashMap<MemoKV> memo_cache_; | 
|  |  | 
|  | // 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) const; | 
|  |  | 
|  | ZoneGrowableArray<BlockEntryInstr*>* headers_; | 
|  | const GrowableArray<BlockEntryInstr*>& preorder_; | 
|  | LoopInfo* top_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(LoopHierarchy); | 
|  | }; | 
|  |  | 
|  | }  // namespace dart | 
|  |  | 
|  | #endif  // RUNTIME_VM_COMPILER_BACKEND_LOOPS_H_ |