| /* |
| * Copyright (C) 2015 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #ifndef ART_COMPILER_OPTIMIZING_INDUCTION_VAR_ANALYSIS_H_ |
| #define ART_COMPILER_OPTIMIZING_INDUCTION_VAR_ANALYSIS_H_ |
| |
| #include <string> |
| |
| #include "nodes.h" |
| #include "optimization.h" |
| |
| namespace art { |
| |
| /** |
| * Induction variable analysis. This class does not have a direct public API. |
| * Instead, the results of induction variable analysis can be queried through |
| * friend classes, such as InductionVarRange. |
| * |
| * The analysis implementation is based on the paper by M. Gerlek et al. |
| * "Beyond Induction Variables: Detecting and Classifying Sequences Using a Demand-Driven SSA Form" |
| * (ACM Transactions on Programming Languages and Systems, Volume 17 Issue 1, Jan. 1995). |
| */ |
| class HInductionVarAnalysis : public HOptimization { |
| public: |
| explicit HInductionVarAnalysis(HGraph* graph, const char* name = kInductionPassName); |
| |
| bool Run() override; |
| |
| static constexpr const char* kInductionPassName = "induction_var_analysis"; |
| |
| private: |
| struct NodeInfo { |
| explicit NodeInfo(uint32_t d) : depth(d), done(false) {} |
| uint32_t depth; |
| bool done; |
| }; |
| |
| enum InductionClass { |
| kInvariant, |
| kLinear, |
| kPolynomial, |
| kGeometric, |
| kWrapAround, |
| kPeriodic |
| }; |
| |
| enum InductionOp { |
| // Operations. |
| kNop, |
| kAdd, |
| kSub, |
| kNeg, |
| kMul, |
| kDiv, |
| kRem, |
| kXor, |
| kFetch, |
| // Trip-counts. |
| kTripCountInLoop, // valid in full loop; loop is finite |
| kTripCountInBody, // valid in body only; loop is finite |
| kTripCountInLoopUnsafe, // valid in full loop; loop may be infinite |
| kTripCountInBodyUnsafe, // valid in body only; loop may be infinite |
| // Comparisons for trip-count tests. |
| kLT, |
| kLE, |
| kGT, |
| kGE |
| }; |
| |
| /** |
| * Defines a detected induction as: |
| * (1) invariant: |
| * op: a + b, a - b, -b, a * b, a / b, a % b, a ^ b, fetch |
| * (2) linear: |
| * nop: a * i + b |
| * (3) polynomial: |
| * nop: sum_lt(a) + b, for linear a |
| * (4) geometric: |
| * op: a * fetch^i + b, a * fetch^-i + b |
| * (5) wrap-around |
| * nop: a, then defined by b |
| * (6) periodic |
| * nop: a, then defined by b (repeated when exhausted) |
| * (7) trip-count: |
| * tc: defined by a, taken-test in b |
| */ |
| struct InductionInfo : public ArenaObject<kArenaAllocInductionVarAnalysis> { |
| InductionInfo(InductionClass ic, |
| InductionOp op, |
| InductionInfo* a, |
| InductionInfo* b, |
| HInstruction* f, |
| DataType::Type t) |
| : induction_class(ic), |
| operation(op), |
| op_a(a), |
| op_b(b), |
| fetch(f), |
| type(t) {} |
| InductionClass induction_class; |
| InductionOp operation; |
| InductionInfo* op_a; |
| InductionInfo* op_b; |
| HInstruction* fetch; |
| DataType::Type type; // precision of operation |
| }; |
| |
| bool IsVisitedNode(HInstruction* instruction) const { |
| return map_.find(instruction) != map_.end(); |
| } |
| |
| InductionInfo* CreateInvariantOp(InductionOp op, InductionInfo* a, InductionInfo* b) { |
| DCHECK(((op != kNeg && a != nullptr) || (op == kNeg && a == nullptr)) && b != nullptr); |
| return CreateSimplifiedInvariant(op, a, b); |
| } |
| |
| InductionInfo* CreateInvariantFetch(HInstruction* f) { |
| DCHECK(f != nullptr); |
| return new (graph_->GetAllocator()) |
| InductionInfo(kInvariant, kFetch, nullptr, nullptr, f, f->GetType()); |
| } |
| |
| InductionInfo* CreateTripCount(InductionOp op, |
| InductionInfo* a, |
| InductionInfo* b, |
| DataType::Type type) { |
| DCHECK(a != nullptr && b != nullptr); |
| return new (graph_->GetAllocator()) InductionInfo(kInvariant, op, a, b, nullptr, type); |
| } |
| |
| InductionInfo* CreateInduction(InductionClass ic, |
| InductionOp op, |
| InductionInfo* a, |
| InductionInfo* b, |
| HInstruction* f, |
| DataType::Type type) { |
| DCHECK(a != nullptr && b != nullptr); |
| return new (graph_->GetAllocator()) InductionInfo(ic, op, a, b, f, type); |
| } |
| |
| // Methods for analysis. |
| void VisitLoop(HLoopInformation* loop); |
| void VisitNode(HLoopInformation* loop, HInstruction* instruction); |
| uint32_t VisitDescendant(HLoopInformation* loop, HInstruction* instruction); |
| void ClassifyTrivial(HLoopInformation* loop, HInstruction* instruction); |
| void ClassifyNonTrivial(HLoopInformation* loop); |
| InductionInfo* RotatePeriodicInduction(InductionInfo* induction, InductionInfo* last); |
| |
| // Transfer operations. |
| InductionInfo* TransferPhi(HLoopInformation* loop, |
| HInstruction* phi, |
| size_t input_index, |
| size_t adjust_input_size); |
| InductionInfo* TransferAddSub(InductionInfo* a, InductionInfo* b, InductionOp op); |
| InductionInfo* TransferNeg(InductionInfo* a); |
| InductionInfo* TransferMul(InductionInfo* a, InductionInfo* b); |
| InductionInfo* TransferConversion(InductionInfo* a, DataType::Type from, DataType::Type to); |
| |
| // Solvers. |
| InductionInfo* SolvePhi(HInstruction* phi, size_t input_index, size_t adjust_input_size); |
| InductionInfo* SolvePhiAllInputs(HLoopInformation* loop, |
| HInstruction* entry_phi, |
| HInstruction* phi); |
| InductionInfo* SolveAddSub(HLoopInformation* loop, |
| HInstruction* entry_phi, |
| HInstruction* instruction, |
| HInstruction* x, |
| HInstruction* y, |
| InductionOp op, |
| bool is_first_call); // possibly swaps x and y to try again |
| InductionInfo* SolveOp(HLoopInformation* loop, |
| HInstruction* entry_phi, |
| HInstruction* instruction, |
| HInstruction* x, |
| HInstruction* y, |
| InductionOp op); |
| InductionInfo* SolveTest(HLoopInformation* loop, |
| HInstruction* entry_phi, |
| HInstruction* instruction, |
| int64_t oppositive_value); |
| InductionInfo* SolveConversion(HLoopInformation* loop, |
| HInstruction* entry_phi, |
| HTypeConversion* conversion); |
| |
| // |
| // Loop trip count analysis methods. |
| // |
| |
| // Trip count information. |
| void VisitControl(HLoopInformation* loop); |
| void VisitCondition(HLoopInformation* loop, |
| HBasicBlock* body, |
| InductionInfo* a, |
| InductionInfo* b, |
| DataType::Type type, |
| IfCondition cmp); |
| void VisitTripCount(HLoopInformation* loop, |
| InductionInfo* lower_expr, |
| InductionInfo* upper_expr, |
| InductionInfo* stride, |
| int64_t stride_value, |
| DataType::Type type, |
| IfCondition cmp); |
| bool IsTaken(InductionInfo* lower_expr, InductionInfo* upper_expr, IfCondition cmp); |
| bool IsFinite(InductionInfo* upper_expr, |
| int64_t stride_value, |
| DataType::Type type, |
| IfCondition cmp); |
| bool FitsNarrowerControl(InductionInfo* lower_expr, |
| InductionInfo* upper_expr, |
| int64_t stride_value, |
| DataType::Type type, |
| IfCondition cmp); |
| bool RewriteBreakLoop(HLoopInformation* loop, |
| HBasicBlock* body, |
| int64_t stride_value, |
| DataType::Type type); |
| |
| // |
| // Helper methods. |
| // |
| |
| // Assign and lookup. |
| void AssignInfo(HLoopInformation* loop, HInstruction* instruction, InductionInfo* info); |
| InductionInfo* LookupInfo(HLoopInformation* loop, HInstruction* instruction); |
| InductionInfo* CreateConstant(int64_t value, DataType::Type type); |
| InductionInfo* CreateSimplifiedInvariant(InductionOp op, InductionInfo* a, InductionInfo* b); |
| HInstruction* GetShiftConstant(HLoopInformation* loop, |
| HInstruction* instruction, |
| InductionInfo* initial); |
| void AssignCycle(HPhi* phi); |
| ArenaSet<HInstruction*>* LookupCycle(HPhi* phi); |
| |
| // Constants. |
| bool IsExact(InductionInfo* info, /*out*/ int64_t* value); |
| bool IsAtMost(InductionInfo* info, /*out*/ int64_t* value); |
| bool IsAtLeast(InductionInfo* info, /*out*/ int64_t* value); |
| |
| // Helpers. |
| static bool IsNarrowingLinear(InductionInfo* info); |
| static bool InductionEqual(InductionInfo* info1, InductionInfo* info2); |
| static std::string FetchToString(HInstruction* fetch); |
| static std::string InductionToString(InductionInfo* info); |
| |
| // TODO: fine tune the following data structures, only keep relevant data. |
| |
| // Temporary book-keeping during the analysis. |
| uint32_t global_depth_; |
| ArenaVector<HInstruction*> stack_; |
| ArenaSafeMap<HInstruction*, NodeInfo> map_; |
| ArenaVector<HInstruction*> scc_; |
| ArenaSafeMap<HInstruction*, InductionInfo*> cycle_; |
| DataType::Type type_; |
| |
| /** |
| * Maintains the results of the analysis as a mapping from loops to a mapping from instructions |
| * to the induction information for that instruction in that loop. |
| */ |
| ArenaSafeMap<HLoopInformation*, ArenaSafeMap<HInstruction*, InductionInfo*>> induction_; |
| |
| /** |
| * Preserves induction cycle information for each loop-phi. |
| */ |
| ArenaSafeMap<HPhi*, ArenaSet<HInstruction*>> cycles_; |
| |
| friend class InductionVarAnalysisTest; |
| friend class InductionVarRange; |
| friend class InductionVarRangeTest; |
| |
| DISALLOW_COPY_AND_ASSIGN(HInductionVarAnalysis); |
| }; |
| |
| } // namespace art |
| |
| #endif // ART_COMPILER_OPTIMIZING_INDUCTION_VAR_ANALYSIS_H_ |