| /* | |
| * Copyright (C) 2018 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_LOOP_ANALYSIS_H_ | |
| #define ART_COMPILER_OPTIMIZING_LOOP_ANALYSIS_H_ | |
| #include "base/macros.h" | |
| #include "nodes.h" | |
| namespace art HIDDEN { | |
| class CodeGenerator; | |
| class InductionVarRange; | |
| class LoopAnalysis; | |
| // Class to hold cached information on properties of the loop. | |
| class LoopAnalysisInfo : public ValueObject { | |
| public: | |
| // No loop unrolling factor (just one copy of the loop-body). | |
| static constexpr uint32_t kNoUnrollingFactor = 1; | |
| // Used for unknown and non-constant trip counts (see InductionVarRange::HasKnownTripCount). | |
| static constexpr int64_t kUnknownTripCount = -1; | |
| explicit LoopAnalysisInfo(HLoopInformation* loop_info) | |
| : trip_count_(kUnknownTripCount), | |
| bb_num_(0), | |
| instr_num_(0), | |
| exits_num_(0), | |
| invariant_exits_num_(0), | |
| has_instructions_preventing_scalar_peeling_(false), | |
| has_instructions_preventing_scalar_unrolling_(false), | |
| has_long_type_instructions_(false), | |
| loop_info_(loop_info) {} | |
| int64_t GetTripCount() const { return trip_count_; } | |
| size_t GetNumberOfBasicBlocks() const { return bb_num_; } | |
| size_t GetNumberOfInstructions() const { return instr_num_; } | |
| size_t GetNumberOfExits() const { return exits_num_; } | |
| size_t GetNumberOfInvariantExits() const { return invariant_exits_num_; } | |
| bool HasInstructionsPreventingScalarPeeling() const { | |
| return has_instructions_preventing_scalar_peeling_; | |
| } | |
| bool HasInstructionsPreventingScalarUnrolling() const { | |
| return has_instructions_preventing_scalar_unrolling_; | |
| } | |
| bool HasInstructionsPreventingScalarOpts() const { | |
| return HasInstructionsPreventingScalarPeeling() || HasInstructionsPreventingScalarUnrolling(); | |
| } | |
| bool HasLongTypeInstructions() const { | |
| return has_long_type_instructions_; | |
| } | |
| HLoopInformation* GetLoopInfo() const { return loop_info_; } | |
| private: | |
| // Trip count of the loop if known, kUnknownTripCount otherwise. | |
| int64_t trip_count_; | |
| // Number of basic blocks in the loop body. | |
| size_t bb_num_; | |
| // Number of instructions in the loop body. | |
| size_t instr_num_; | |
| // Number of loop's exits. | |
| size_t exits_num_; | |
| // Number of "if" loop exits (with HIf instruction) whose condition is loop-invariant. | |
| size_t invariant_exits_num_; | |
| // Whether the loop has instructions which make scalar loop peeling non-beneficial. | |
| bool has_instructions_preventing_scalar_peeling_; | |
| // Whether the loop has instructions which make scalar loop unrolling non-beneficial. | |
| bool has_instructions_preventing_scalar_unrolling_; | |
| // Whether the loop has instructions of primitive long type; unrolling these loop will | |
| // likely introduce spill/fills on 32-bit targets. | |
| bool has_long_type_instructions_; | |
| // Corresponding HLoopInformation. | |
| HLoopInformation* loop_info_; | |
| friend class LoopAnalysis; | |
| }; | |
| // Placeholder class for methods and routines used to analyse loops, calculate loop properties | |
| // and characteristics. | |
| class LoopAnalysis : public ValueObject { | |
| public: | |
| // Calculates loops basic properties like body size, exits number, etc. and fills | |
| // 'analysis_results' with this information. | |
| static void CalculateLoopBasicProperties(HLoopInformation* loop_info, | |
| LoopAnalysisInfo* analysis_results, | |
| int64_t trip_count); | |
| // Returns the trip count of the loop if it is known and kUnknownTripCount otherwise. | |
| static int64_t GetLoopTripCount(HLoopInformation* loop_info, | |
| const InductionVarRange* induction_range); | |
| private: | |
| // Returns whether an instruction makes scalar loop peeling/unrolling non-beneficial. | |
| // | |
| // If in the loop body we have a dex/runtime call then its contribution to the whole | |
| // loop performance will probably prevail. So peeling/unrolling optimization will not bring | |
| // any noticeable performance improvement. It will increase the code size. | |
| static bool MakesScalarPeelingUnrollingNonBeneficial(HInstruction* instruction) { | |
| return (instruction->IsNewArray() || | |
| instruction->IsNewInstance() || | |
| instruction->IsUnresolvedInstanceFieldGet() || | |
| instruction->IsUnresolvedInstanceFieldSet() || | |
| instruction->IsUnresolvedStaticFieldGet() || | |
| instruction->IsUnresolvedStaticFieldSet() || | |
| // TODO: Support loops with intrinsified invokes. | |
| instruction->IsInvoke()); | |
| } | |
| }; | |
| // | |
| // Helper class which holds target-dependent methods and constants needed for loop optimizations. | |
| // | |
| // To support peeling/unrolling for a new architecture one needs to create new helper class, | |
| // inherit it from this and add implementation for the following methods. | |
| // | |
| class ArchNoOptsLoopHelper : public ArenaObject<kArenaAllocOptimization> { | |
| public: | |
| explicit ArchNoOptsLoopHelper(const CodeGenerator& codegen) : codegen_(codegen) {} | |
| virtual ~ArchNoOptsLoopHelper() {} | |
| // Creates an instance of specialised helper for the target or default helper if the target | |
| // doesn't support loop peeling and unrolling. | |
| static ArchNoOptsLoopHelper* Create(const CodeGenerator& codegen, ArenaAllocator* allocator); | |
| // Returns whether the loop is not beneficial for loop peeling/unrolling. | |
| // | |
| // For example, if the loop body has too many instructions then peeling/unrolling optimization | |
| // will not bring any noticeable performance improvement however will increase the code size. | |
| // | |
| // Returns 'true' by default, should be overridden by particular target loop helper. | |
| virtual bool IsLoopNonBeneficialForScalarOpts( | |
| [[maybe_unused]] LoopAnalysisInfo* loop_analysis_info) const { | |
| return true; | |
| } | |
| // Returns optimal scalar unrolling factor for the loop. | |
| // | |
| // Returns kNoUnrollingFactor by default, should be overridden by particular target loop helper. | |
| virtual uint32_t GetScalarUnrollingFactor( | |
| [[maybe_unused]] const LoopAnalysisInfo* analysis_info) const { | |
| return LoopAnalysisInfo::kNoUnrollingFactor; | |
| } | |
| // Returns whether scalar loop peeling is enabled, | |
| // | |
| // Returns 'false' by default, should be overridden by particular target loop helper. | |
| virtual bool IsLoopPeelingEnabled() const { return false; } | |
| // Returns whether it is beneficial to fully unroll the loop. | |
| // | |
| // Returns 'false' by default, should be overridden by particular target loop helper. | |
| virtual bool IsFullUnrollingBeneficial([[maybe_unused]] LoopAnalysisInfo* analysis_info) const { | |
| return false; | |
| } | |
| // Returns optimal SIMD unrolling factor for the loop. | |
| // | |
| // Returns kNoUnrollingFactor by default, should be overridden by particular target loop helper. | |
| virtual uint32_t GetSIMDUnrollingFactor([[maybe_unused]] HBasicBlock* block, | |
| [[maybe_unused]] int64_t trip_count, | |
| [[maybe_unused]] uint32_t max_peel, | |
| [[maybe_unused]] uint32_t vector_length) const { | |
| return LoopAnalysisInfo::kNoUnrollingFactor; | |
| } | |
| protected: | |
| const CodeGenerator& codegen_; | |
| }; | |
| } // namespace art | |
| #endif // ART_COMPILER_OPTIMIZING_LOOP_ANALYSIS_H_ |