1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifndef ART_COMPILER_OPTIMIZING_LOOP_ANALYSIS_H_
18 #define ART_COMPILER_OPTIMIZING_LOOP_ANALYSIS_H_
19 
20 #include "nodes.h"
21 
22 namespace art {
23 
24 class CodeGenerator;
25 class InductionVarRange;
26 class LoopAnalysis;
27 
28 // Class to hold cached information on properties of the loop.
29 class LoopAnalysisInfo : public ValueObject {
30  public:
31   // No loop unrolling factor (just one copy of the loop-body).
32   static constexpr uint32_t kNoUnrollingFactor = 1;
33   // Used for unknown and non-constant trip counts (see InductionVarRange::HasKnownTripCount).
34   static constexpr int64_t kUnknownTripCount = -1;
35 
LoopAnalysisInfo(HLoopInformation * loop_info)36   explicit LoopAnalysisInfo(HLoopInformation* loop_info)
37       : trip_count_(kUnknownTripCount),
38         bb_num_(0),
39         instr_num_(0),
40         exits_num_(0),
41         invariant_exits_num_(0),
42         has_instructions_preventing_scalar_peeling_(false),
43         has_instructions_preventing_scalar_unrolling_(false),
44         has_long_type_instructions_(false),
45         loop_info_(loop_info) {}
46 
GetTripCount()47   int64_t GetTripCount() const { return trip_count_; }
GetNumberOfBasicBlocks()48   size_t GetNumberOfBasicBlocks() const { return bb_num_; }
GetNumberOfInstructions()49   size_t GetNumberOfInstructions() const { return instr_num_; }
GetNumberOfExits()50   size_t GetNumberOfExits() const { return exits_num_; }
GetNumberOfInvariantExits()51   size_t GetNumberOfInvariantExits() const { return invariant_exits_num_; }
52 
HasInstructionsPreventingScalarPeeling()53   bool HasInstructionsPreventingScalarPeeling() const {
54     return has_instructions_preventing_scalar_peeling_;
55   }
56 
HasInstructionsPreventingScalarUnrolling()57   bool HasInstructionsPreventingScalarUnrolling() const {
58     return has_instructions_preventing_scalar_unrolling_;
59   }
60 
HasInstructionsPreventingScalarOpts()61   bool HasInstructionsPreventingScalarOpts() const {
62     return HasInstructionsPreventingScalarPeeling() || HasInstructionsPreventingScalarUnrolling();
63   }
64 
HasLongTypeInstructions()65   bool HasLongTypeInstructions() const {
66     return has_long_type_instructions_;
67   }
68 
GetLoopInfo()69   HLoopInformation* GetLoopInfo() const { return loop_info_; }
70 
71  private:
72   // Trip count of the loop if known, kUnknownTripCount otherwise.
73   int64_t trip_count_;
74   // Number of basic blocks in the loop body.
75   size_t bb_num_;
76   // Number of instructions in the loop body.
77   size_t instr_num_;
78   // Number of loop's exits.
79   size_t exits_num_;
80   // Number of "if" loop exits (with HIf instruction) whose condition is loop-invariant.
81   size_t invariant_exits_num_;
82   // Whether the loop has instructions which make scalar loop peeling non-beneficial.
83   bool has_instructions_preventing_scalar_peeling_;
84   // Whether the loop has instructions which make scalar loop unrolling non-beneficial.
85   bool has_instructions_preventing_scalar_unrolling_;
86   // Whether the loop has instructions of primitive long type; unrolling these loop will
87   // likely introduce spill/fills on 32-bit targets.
88   bool has_long_type_instructions_;
89 
90   // Corresponding HLoopInformation.
91   HLoopInformation* loop_info_;
92 
93   friend class LoopAnalysis;
94 };
95 
96 // Placeholder class for methods and routines used to analyse loops, calculate loop properties
97 // and characteristics.
98 class LoopAnalysis : public ValueObject {
99  public:
100   // Calculates loops basic properties like body size, exits number, etc. and fills
101   // 'analysis_results' with this information.
102   static void CalculateLoopBasicProperties(HLoopInformation* loop_info,
103                                            LoopAnalysisInfo* analysis_results,
104                                            int64_t trip_count);
105 
106   // Returns the trip count of the loop if it is known and kUnknownTripCount otherwise.
107   static int64_t GetLoopTripCount(HLoopInformation* loop_info,
108                                   const InductionVarRange* induction_range);
109 
110  private:
111   // Returns whether an instruction makes scalar loop peeling/unrolling non-beneficial.
112   //
113   // If in the loop body we have a dex/runtime call then its contribution to the whole
114   // loop performance will probably prevail. So peeling/unrolling optimization will not bring
115   // any noticeable performance improvement. It will increase the code size.
MakesScalarPeelingUnrollingNonBeneficial(HInstruction * instruction)116   static bool MakesScalarPeelingUnrollingNonBeneficial(HInstruction* instruction) {
117     return (instruction->IsNewArray() ||
118         instruction->IsNewInstance() ||
119         instruction->IsUnresolvedInstanceFieldGet() ||
120         instruction->IsUnresolvedInstanceFieldSet() ||
121         instruction->IsUnresolvedStaticFieldGet() ||
122         instruction->IsUnresolvedStaticFieldSet() ||
123         // TODO: Support loops with intrinsified invokes.
124         instruction->IsInvoke());
125   }
126 };
127 
128 //
129 // Helper class which holds target-dependent methods and constants needed for loop optimizations.
130 //
131 // To support peeling/unrolling for a new architecture one needs to create new helper class,
132 // inherit it from this and add implementation for the following methods.
133 //
134 class ArchNoOptsLoopHelper : public ArenaObject<kArenaAllocOptimization> {
135  public:
ArchNoOptsLoopHelper(const CodeGenerator & codegen)136   explicit ArchNoOptsLoopHelper(const CodeGenerator& codegen) : codegen_(codegen) {}
~ArchNoOptsLoopHelper()137   virtual ~ArchNoOptsLoopHelper() {}
138 
139   // Creates an instance of specialised helper for the target or default helper if the target
140   // doesn't support loop peeling and unrolling.
141   static ArchNoOptsLoopHelper* Create(const CodeGenerator& codegen, ArenaAllocator* allocator);
142 
143   // Returns whether the loop is not beneficial for loop peeling/unrolling.
144   //
145   // For example, if the loop body has too many instructions then peeling/unrolling optimization
146   // will not bring any noticeable performance improvement however will increase the code size.
147   //
148   // Returns 'true' by default, should be overridden by particular target loop helper.
IsLoopNonBeneficialForScalarOpts(LoopAnalysisInfo * loop_analysis_info ATTRIBUTE_UNUSED)149   virtual bool IsLoopNonBeneficialForScalarOpts(
150       LoopAnalysisInfo* loop_analysis_info ATTRIBUTE_UNUSED) const { return true; }
151 
152   // Returns optimal scalar unrolling factor for the loop.
153   //
154   // Returns kNoUnrollingFactor by default, should be overridden by particular target loop helper.
GetScalarUnrollingFactor(const LoopAnalysisInfo * analysis_info ATTRIBUTE_UNUSED)155   virtual uint32_t GetScalarUnrollingFactor(
156       const LoopAnalysisInfo* analysis_info ATTRIBUTE_UNUSED) const {
157     return LoopAnalysisInfo::kNoUnrollingFactor;
158   }
159 
160   // Returns whether scalar loop peeling is enabled,
161   //
162   // Returns 'false' by default, should be overridden by particular target loop helper.
IsLoopPeelingEnabled()163   virtual bool IsLoopPeelingEnabled() const { return false; }
164 
165   // Returns whether it is beneficial to fully unroll the loop.
166   //
167   // Returns 'false' by default, should be overridden by particular target loop helper.
IsFullUnrollingBeneficial(LoopAnalysisInfo * analysis_info ATTRIBUTE_UNUSED)168   virtual bool IsFullUnrollingBeneficial(LoopAnalysisInfo* analysis_info ATTRIBUTE_UNUSED) const {
169     return false;
170   }
171 
172   // Returns optimal SIMD unrolling factor for the loop.
173   //
174   // Returns kNoUnrollingFactor by default, should be overridden by particular target loop helper.
GetSIMDUnrollingFactor(HBasicBlock * block ATTRIBUTE_UNUSED,int64_t trip_count ATTRIBUTE_UNUSED,uint32_t max_peel ATTRIBUTE_UNUSED,uint32_t vector_length ATTRIBUTE_UNUSED)175   virtual uint32_t GetSIMDUnrollingFactor(HBasicBlock* block ATTRIBUTE_UNUSED,
176                                           int64_t trip_count ATTRIBUTE_UNUSED,
177                                           uint32_t max_peel ATTRIBUTE_UNUSED,
178                                           uint32_t vector_length ATTRIBUTE_UNUSED) const {
179     return LoopAnalysisInfo::kNoUnrollingFactor;
180   }
181 
182  protected:
183   const CodeGenerator& codegen_;
184 };
185 
186 }  // namespace art
187 
188 #endif  // ART_COMPILER_OPTIMIZING_LOOP_ANALYSIS_H_
189