331 lines
13 KiB
C++
331 lines
13 KiB
C++
/*
|
|
* 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 "base/arena_containers.h"
|
|
#include "base/array_ref.h"
|
|
#include "base/scoped_arena_containers.h"
|
|
#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;
|
|
struct StackEntry;
|
|
|
|
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
|
|
};
|
|
|
|
|
|
InductionInfo* CreateInvariantOp(const HBasicBlock* context,
|
|
const HLoopInformation* loop,
|
|
InductionOp op,
|
|
InductionInfo* a,
|
|
InductionInfo* b) {
|
|
DCHECK(((op != kNeg && a != nullptr) || (op == kNeg && a == nullptr)) && b != nullptr);
|
|
return CreateSimplifiedInvariant(context, loop, 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(const HLoopInformation* loop);
|
|
size_t TryVisitNodes(const HLoopInformation* loop,
|
|
HInstruction* start_instruction,
|
|
size_t global_depth,
|
|
/*inout*/ ScopedArenaSafeMap<HInstruction*, NodeInfo>* visited_instructions);
|
|
void ExtractScc(ArrayRef<const StackEntry> stack_tail, ScopedArenaVector<HInstruction*>* scc);
|
|
void ClassifyTrivial(const HLoopInformation* loop, HInstruction* instruction);
|
|
void ClassifyNonTrivial(const HLoopInformation* loop, ArrayRef<const StackEntry> stack_tail);
|
|
InductionInfo* RotatePeriodicInduction(InductionInfo* induction,
|
|
InductionInfo* last,
|
|
DataType::Type type);
|
|
|
|
// Transfer operations.
|
|
InductionInfo* TransferPhi(const HLoopInformation* loop,
|
|
HInstruction* phi,
|
|
size_t input_index,
|
|
size_t adjust_input_size);
|
|
InductionInfo* TransferAddSub(const HBasicBlock* context,
|
|
const HLoopInformation* loop,
|
|
InductionInfo* a,
|
|
InductionInfo* b,
|
|
InductionOp op,
|
|
DataType::Type type);
|
|
InductionInfo* TransferNeg(const HBasicBlock* context,
|
|
const HLoopInformation* loop,
|
|
InductionInfo* a,
|
|
DataType::Type type);
|
|
InductionInfo* TransferMul(const HBasicBlock* context,
|
|
const HLoopInformation* loop,
|
|
InductionInfo* a,
|
|
InductionInfo* b,
|
|
DataType::Type type);
|
|
InductionInfo* TransferConversion(InductionInfo* a, DataType::Type from, DataType::Type to);
|
|
|
|
// Solvers.
|
|
InductionInfo* SolvePhi(HInstruction* phi,
|
|
size_t input_index,
|
|
size_t adjust_input_size,
|
|
const ScopedArenaSafeMap<HInstruction*, InductionInfo*>& cycle);
|
|
InductionInfo* SolvePhiAllInputs(const HLoopInformation* loop,
|
|
HInstruction* entry_phi,
|
|
HInstruction* phi,
|
|
const ScopedArenaSafeMap<HInstruction*, InductionInfo*>& cycle,
|
|
DataType::Type type);
|
|
InductionInfo* SolveAddSub(const HLoopInformation* loop,
|
|
HInstruction* entry_phi,
|
|
HInstruction* instruction,
|
|
HInstruction* x,
|
|
HInstruction* y,
|
|
InductionOp op,
|
|
const ScopedArenaSafeMap<HInstruction*, InductionInfo*>& cycle,
|
|
DataType::Type type);
|
|
InductionInfo* SolveOp(const HLoopInformation* loop,
|
|
HInstruction* entry_phi,
|
|
HInstruction* instruction,
|
|
HInstruction* x,
|
|
HInstruction* y,
|
|
InductionOp op,
|
|
DataType::Type type);
|
|
InductionInfo* SolveTest(const HLoopInformation* loop,
|
|
HInstruction* entry_phi,
|
|
HInstruction* instruction,
|
|
int64_t opposite_value,
|
|
DataType::Type type);
|
|
InductionInfo* SolveConversion(const HLoopInformation* loop,
|
|
HInstruction* entry_phi,
|
|
HTypeConversion* conversion,
|
|
const ScopedArenaSafeMap<HInstruction*, InductionInfo*>& cycle,
|
|
/*inout*/ DataType::Type* type);
|
|
|
|
//
|
|
// Loop trip count analysis methods.
|
|
//
|
|
|
|
// Trip count information.
|
|
void VisitControl(const HLoopInformation* loop);
|
|
void VisitCondition(const HBasicBlock* context,
|
|
const HLoopInformation* loop,
|
|
HBasicBlock* body,
|
|
InductionInfo* a,
|
|
InductionInfo* b,
|
|
DataType::Type type,
|
|
IfCondition cmp);
|
|
void VisitTripCount(const HBasicBlock* context,
|
|
const HLoopInformation* loop,
|
|
InductionInfo* lower_expr,
|
|
InductionInfo* upper_expr,
|
|
InductionInfo* stride,
|
|
int64_t stride_value,
|
|
DataType::Type type,
|
|
IfCondition cmp);
|
|
bool IsTaken(const HBasicBlock* context,
|
|
const HLoopInformation* loop,
|
|
InductionInfo* lower_expr,
|
|
InductionInfo* upper_expr,
|
|
IfCondition cmp);
|
|
bool IsFinite(const HBasicBlock* context,
|
|
const HLoopInformation* loop,
|
|
InductionInfo* upper_expr,
|
|
int64_t stride_value,
|
|
DataType::Type type,
|
|
IfCondition cmp);
|
|
bool FitsNarrowerControl(const HBasicBlock* context,
|
|
const HLoopInformation* loop,
|
|
InductionInfo* lower_expr,
|
|
InductionInfo* upper_expr,
|
|
int64_t stride_value,
|
|
DataType::Type type,
|
|
IfCondition cmp);
|
|
bool RewriteBreakLoop(const HBasicBlock* context,
|
|
const HLoopInformation* loop,
|
|
HBasicBlock* body,
|
|
int64_t stride_value,
|
|
DataType::Type type);
|
|
|
|
//
|
|
// Helper methods.
|
|
//
|
|
|
|
// Assign and lookup.
|
|
void AssignInfo(const HLoopInformation* loop, HInstruction* instruction, InductionInfo* info);
|
|
InductionInfo* LookupInfo(const HLoopInformation* loop, HInstruction* instruction);
|
|
InductionInfo* CreateConstant(int64_t value, DataType::Type type);
|
|
InductionInfo* CreateSimplifiedInvariant(const HBasicBlock* context,
|
|
const HLoopInformation* loop,
|
|
InductionOp op,
|
|
InductionInfo* a,
|
|
InductionInfo* b);
|
|
HInstruction* GetShiftConstant(const HLoopInformation* loop,
|
|
HInstruction* instruction,
|
|
InductionInfo* initial);
|
|
void AssignCycle(HPhi* phi, ArrayRef<HInstruction* const> scc);
|
|
ArenaSet<HInstruction*>* LookupCycle(HPhi* phi);
|
|
|
|
// Constants.
|
|
bool IsExact(const HBasicBlock* context,
|
|
const HLoopInformation* loop,
|
|
InductionInfo* info,
|
|
/*out*/int64_t* value);
|
|
bool IsAtMost(const HBasicBlock* context,
|
|
const HLoopInformation* loop,
|
|
InductionInfo* info,
|
|
/*out*/int64_t* value);
|
|
bool IsAtLeast(const HBasicBlock* context,
|
|
const HLoopInformation* loop,
|
|
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);
|
|
|
|
/**
|
|
* 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<const 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_
|