Added geometric induction variables analysis.
Rationale:
Information on geometric and polynomial (coming soon) sequences
are nice to have to further enhance BCE and last-value assignment.
Test: test-art-host
Change-Id: Ib5e2998c3eb1009def6fd00b82935da7c3ba7c6e
diff --git a/compiler/optimizing/induction_var_range.cc b/compiler/optimizing/induction_var_range.cc
index 235793d..75619a3 100644
--- a/compiler/optimizing/induction_var_range.cc
+++ b/compiler/optimizing/induction_var_range.cc
@@ -57,6 +57,21 @@
return false;
}
+/** Returns b^e for b,e >= 1. */
+static int64_t IntPow(int64_t b, int64_t e) {
+ DCHECK_GE(b, 1);
+ DCHECK_GE(e, 1);
+ int64_t pow = 1;
+ while (e) {
+ if (e & 1) {
+ pow *= b;
+ }
+ e >>= 1;
+ b *= b;
+ }
+ return pow;
+}
+
/**
* Detects an instruction that is >= 0. As long as the value is carried by
* a single instruction, arithmetic wrap-around cannot occur.
@@ -399,6 +414,8 @@
/*out*/ HLoopInformation** loop,
/*out*/ HInductionVarAnalysis::InductionInfo** info,
/*out*/ HInductionVarAnalysis::InductionInfo** trip) const {
+ DCHECK(context != nullptr);
+ DCHECK(context->GetBlock() != nullptr);
HLoopInformation* lp = context->GetBlock()->GetLoopInformation(); // closest enveloping loop
if (lp != nullptr) {
HInductionVarAnalysis::InductionInfo* i = induction_analysis_->LookupInfo(lp, instruction);
@@ -474,6 +491,8 @@
HInductionVarAnalysis::InductionInfo* trip,
bool in_body,
bool is_min) const {
+ DCHECK(info != nullptr);
+ DCHECK(info->induction_class == HInductionVarAnalysis::kLinear);
// Detect common situation where an offset inside the trip-count cancels out during range
// analysis (finding max a * (TC - 1) + OFFSET for a == 1 and TC = UPPER - OFFSET or finding
// min a * (TC - 1) + OFFSET for a == -1 and TC = OFFSET - UPPER) to avoid losing information
@@ -520,6 +539,30 @@
GetVal(info->op_b, trip, in_body, is_min));
}
+InductionVarRange::Value InductionVarRange::GetGeometric(HInductionVarAnalysis::InductionInfo* info,
+ HInductionVarAnalysis::InductionInfo* trip,
+ bool in_body,
+ bool is_min) const {
+ DCHECK(info != nullptr);
+ DCHECK(info->induction_class == HInductionVarAnalysis::kGeometric);
+ int64_t a = 0;
+ int64_t f = 0;
+ if (IsConstant(info->op_a, kExact, &a) &&
+ CanLongValueFitIntoInt(a) &&
+ IsIntAndGet(info->fetch, &f) &&
+ CanLongValueFitIntoInt(f) && f >= 1) {
+ // Conservative bounds on a * f^-i + b with f >= 1 can be computed without trip count.
+ // Same for mod. All other forms would require a much more elaborate evaluation.
+ const bool is_min_a = a >= 0 ? is_min : !is_min;
+ if (info->operation == HInductionVarAnalysis::kDiv) {
+ return AddValue(Value(is_min_a ? 0 : a), GetVal(info->op_b, trip, in_body, is_min));
+ } else if (info->operation == HInductionVarAnalysis::kNop) {
+ return AddValue(Value(is_min_a ? (a % f) : a), GetVal(info->op_b, trip, in_body, is_min));
+ }
+ }
+ return Value();
+}
+
InductionVarRange::Value InductionVarRange::GetFetch(HInstruction* instruction,
HInductionVarAnalysis::InductionInfo* trip,
bool in_body,
@@ -631,6 +674,10 @@
break;
case HInductionVarAnalysis::kLinear:
return CorrectForType(GetLinear(info, trip, in_body, is_min), info->type);
+ case HInductionVarAnalysis::kPolynomial:
+ break;
+ case HInductionVarAnalysis::kGeometric:
+ return GetGeometric(info, trip, in_body, is_min);
case HInductionVarAnalysis::kWrapAround:
case HInductionVarAnalysis::kPeriodic:
return MergeVal(GetVal(info->op_a, trip, in_body, is_min),
@@ -842,17 +889,21 @@
*needs_taken_test = IsBodyTripCount(trip);
// Handle last value request.
if (is_last_value) {
- if (info->induction_class == HInductionVarAnalysis::kLinear) {
- if (*stride_value > 0) {
- lower = nullptr;
- } else {
- upper = nullptr;
- }
- } else if (info->induction_class == HInductionVarAnalysis::kPeriodic) {
- DCHECK(!in_body);
- return GenerateLastValuePeriodic(info, trip, graph, block, lower, needs_taken_test);
- } else {
- return false;
+ DCHECK(!in_body);
+ switch (info->induction_class) {
+ case HInductionVarAnalysis::kLinear:
+ if (*stride_value > 0) {
+ lower = nullptr;
+ } else {
+ upper = nullptr;
+ }
+ break;
+ case HInductionVarAnalysis::kGeometric:
+ return GenerateLastValueGeometric(info, trip, graph, block, lower);
+ case HInductionVarAnalysis::kPeriodic:
+ return GenerateLastValuePeriodic(info, trip, graph, block, lower, needs_taken_test);
+ default:
+ return false;
}
}
// Code generation for taken test: generate the code when requested or otherwise analyze
@@ -874,12 +925,68 @@
GenerateCode(info, trip, graph, block, upper, in_body, /* is_min */ false);
}
+bool InductionVarRange::GenerateLastValueGeometric(HInductionVarAnalysis::InductionInfo* info,
+ HInductionVarAnalysis::InductionInfo* trip,
+ HGraph* graph,
+ HBasicBlock* block,
+ /*out*/HInstruction** result) const {
+ DCHECK(info != nullptr);
+ DCHECK(info->induction_class == HInductionVarAnalysis::kGeometric);
+ // Detect known base and trip count (always taken).
+ int64_t f = 0;
+ int64_t t = 0;
+ if (IsIntAndGet(info->fetch, &f) && f >= 1 && IsConstant(trip->op_a, kExact, &t) && t >= 1) {
+ HInstruction* opa = nullptr;
+ HInstruction* opb = nullptr;
+ if (GenerateCode(info->op_a, nullptr, graph, block, &opa, false, false) &&
+ GenerateCode(info->op_b, nullptr, graph, block, &opb, false, false)) {
+ // Generate a % f + b.
+ if (info->operation == HInductionVarAnalysis::kNop) {
+ if (graph != nullptr) {
+ HInstruction* rem =
+ Insert(block, new (graph->GetArena()) HRem(info->type, opa, info->fetch, kNoDexPc));
+ *result = Insert(block, new (graph->GetArena()) HAdd(info->type, rem, opb));
+ }
+ return true;
+ }
+ // Compute f ^ t.
+ int64_t fpowt = IntPow(f, t);
+ if (graph != nullptr) {
+ DCHECK(info->type == Primitive::kPrimInt); // due to codegen, generalize?
+ if (fpowt == 0) {
+ // Special case: repeated mul/div always yields zero.
+ *result = graph->GetIntConstant(0);
+ } else if (info->operation == HInductionVarAnalysis::kMul) {
+ // Last value multiplication: a * f ^ t + b.
+ HInstruction* mul = Insert(block,
+ new (graph->GetArena()) HMul(info->type,
+ opa,
+ graph->GetIntConstant(fpowt)));
+ *result = Insert(block, new (graph->GetArena()) HAdd(info->type, mul, opb));
+ } else {
+ // Last value multiplication: a * f ^ -t + b.
+ DCHECK_EQ(info->operation, HInductionVarAnalysis::kDiv);
+ HInstruction* div = Insert(block,
+ new (graph->GetArena()) HDiv(info->type,
+ opa,
+ graph->GetIntConstant(fpowt),
+ kNoDexPc));
+ *result = Insert(block, new (graph->GetArena()) HAdd(info->type, div, opb));
+ }
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
bool InductionVarRange::GenerateLastValuePeriodic(HInductionVarAnalysis::InductionInfo* info,
HInductionVarAnalysis::InductionInfo* trip,
HGraph* graph,
HBasicBlock* block,
/*out*/HInstruction** result,
/*out*/bool* needs_taken_test) const {
+ DCHECK(info != nullptr);
DCHECK(info->induction_class == HInductionVarAnalysis::kPeriodic);
// Count period.
int32_t period = 1;
@@ -937,6 +1044,7 @@
return true;
}
// Verify type safety.
+ // TODO: generalize
Primitive::Type type = Primitive::kPrimInt;
if (info->type != Primitive::kPrimInt && info->type != Primitive::kPrimBoolean) {
return false;
@@ -1058,6 +1166,9 @@
}
break;
}
+ case HInductionVarAnalysis::kPolynomial:
+ case HInductionVarAnalysis::kGeometric:
+ break;
case HInductionVarAnalysis::kWrapAround:
case HInductionVarAnalysis::kPeriodic: {
// Wrap-around and periodic inductions are restricted to constants only, so that extreme
@@ -1071,8 +1182,6 @@
}
break;
}
- default:
- break;
}
}
return false;