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_analysis.cc b/compiler/optimizing/induction_var_analysis.cc
index f2602fb..1561502 100644
--- a/compiler/optimizing/induction_var_analysis.cc
+++ b/compiler/optimizing/induction_var_analysis.cc
@@ -218,20 +218,21 @@
} else if (instruction->IsSub()) {
info = TransferAddSub(LookupInfo(loop, instruction->InputAt(0)),
LookupInfo(loop, instruction->InputAt(1)), kSub);
+ } else if (instruction->IsNeg()) {
+ info = TransferNeg(LookupInfo(loop, instruction->InputAt(0)));
} else if (instruction->IsMul()) {
info = TransferMul(LookupInfo(loop, instruction->InputAt(0)),
LookupInfo(loop, instruction->InputAt(1)));
} else if (instruction->IsShl()) {
- info = TransferShl(LookupInfo(loop, instruction->InputAt(0)),
- LookupInfo(loop, instruction->InputAt(1)),
- instruction->InputAt(0)->GetType());
- } else if (instruction->IsNeg()) {
- info = TransferNeg(LookupInfo(loop, instruction->InputAt(0)));
+ HInstruction* mulc = GetMultConstantForShift(loop, instruction);
+ if (mulc != nullptr) {
+ info = TransferMul(LookupInfo(loop, instruction->InputAt(0)),
+ LookupInfo(loop, mulc));
+ }
} else if (instruction->IsTypeConversion()) {
info = TransferCnv(LookupInfo(loop, instruction->InputAt(0)),
instruction->AsTypeConversion()->GetInputType(),
instruction->AsTypeConversion()->GetResultType());
-
} else if (instruction->IsBoundsCheck()) {
info = LookupInfo(loop, instruction->InputAt(0)); // Pass-through.
}
@@ -271,7 +272,12 @@
if (size == 1) {
InductionInfo* update = TransferPhi(loop, phi, /* input_index */ 1);
if (update != nullptr) {
- AssignInfo(loop, phi, CreateInduction(kWrapAround, initial, update, type_));
+ AssignInfo(loop, phi, CreateInduction(kWrapAround,
+ kNop,
+ initial,
+ update,
+ /*fetch*/ nullptr,
+ type_));
}
return;
}
@@ -289,6 +295,20 @@
} else if (instruction->IsSub()) {
update = SolveAddSub(
loop, phi, instruction, instruction->InputAt(0), instruction->InputAt(1), kSub, true);
+ } else if (instruction->IsMul()) {
+ update = SolveGeo(
+ loop, phi, instruction, instruction->InputAt(0), instruction->InputAt(1), kMul);
+ } else if (instruction->IsDiv()) {
+ update = SolveGeo(
+ loop, phi, instruction, instruction->InputAt(0), instruction->InputAt(1), kDiv);
+ } else if (instruction->IsRem()) {
+ update = SolveGeo(
+ loop, phi, instruction, instruction->InputAt(0), instruction->InputAt(1), kNop);
+ } else if (instruction->IsShl()) {
+ HInstruction* mulc = GetMultConstantForShift(loop, instruction);
+ if (mulc != nullptr) {
+ update = SolveGeo(loop, phi, instruction, instruction->InputAt(0), mulc, kMul);
+ }
} else if (instruction->IsXor()) {
update = SolveXor(loop, phi, instruction, instruction->InputAt(0), instruction->InputAt(1));
} else if (instruction->IsEqual()) {
@@ -309,9 +329,14 @@
if (induction != nullptr) {
switch (induction->induction_class) {
case kInvariant:
+ // Construct combined stride of the linear induction.
+ induction = CreateInduction(kLinear, kNop, induction, initial, /*fetch*/ nullptr, type_);
+ FALLTHROUGH_INTENDED;
+ case kPolynomial:
+ case kGeometric:
// Classify first phi and then the rest of the cycle "on-demand".
// Statements are scanned in order.
- AssignInfo(loop, phi, CreateInduction(kLinear, induction, initial, type_));
+ AssignInfo(loop, phi, induction);
for (size_t i = 1; i < size; i++) {
ClassifyTrivial(loop, scc_[i]);
}
@@ -341,10 +366,19 @@
// (b, c, d, e, a)
// in preparation of assigning this to the previous variable in the sequence.
if (induction->induction_class == kInvariant) {
- return CreateInduction(kPeriodic, induction, last, type_);
+ return CreateInduction(kPeriodic,
+ kNop,
+ induction,
+ last,
+ /*fetch*/ nullptr,
+ type_);
}
- return CreateInduction(
- kPeriodic, induction->op_a, RotatePeriodicInduction(induction->op_b, last), type_);
+ return CreateInduction(kPeriodic,
+ kNop,
+ induction->op_a,
+ RotatePeriodicInduction(induction->op_b, last),
+ /*fetch*/ nullptr,
+ type_);
}
HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferPhi(HLoopInformation* loop,
@@ -366,36 +400,55 @@
HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferAddSub(InductionInfo* a,
InductionInfo* b,
InductionOp op) {
- // Transfer over an addition or subtraction: any invariant, linear, wrap-around, or periodic
- // can be combined with an invariant to yield a similar result. Even two linear inputs can
- // be combined. All other combinations fail, however.
+ // Transfer over an addition or subtraction: any invariant, linear, polynomial, geometric,
+ // wrap-around, or periodic can be combined with an invariant to yield a similar result.
+ // Even two linear inputs can be combined. Other combinations fail.
if (a != nullptr && b != nullptr) {
type_ = Narrowest(type_, Narrowest(a->type, b->type));
if (a->induction_class == kInvariant && b->induction_class == kInvariant) {
return CreateInvariantOp(op, a, b);
} else if (a->induction_class == kLinear && b->induction_class == kLinear) {
return CreateInduction(kLinear,
+ kNop,
TransferAddSub(a->op_a, b->op_a, op),
TransferAddSub(a->op_b, b->op_b, op),
+ /*fetch*/ nullptr,
type_);
} else if (a->induction_class == kInvariant) {
InductionInfo* new_a = b->op_a;
InductionInfo* new_b = TransferAddSub(a, b->op_b, op);
- if (b->induction_class != kLinear) {
- DCHECK(b->induction_class == kWrapAround || b->induction_class == kPeriodic);
+ if (b->induction_class == kWrapAround || b->induction_class == kPeriodic) {
new_a = TransferAddSub(a, new_a, op);
} else if (op == kSub) { // Negation required.
new_a = TransferNeg(new_a);
}
- return CreateInduction(b->induction_class, new_a, new_b, type_);
+ return CreateInduction(b->induction_class, b->operation, new_a, new_b, b->fetch, type_);
} else if (b->induction_class == kInvariant) {
InductionInfo* new_a = a->op_a;
InductionInfo* new_b = TransferAddSub(a->op_b, b, op);
- if (a->induction_class != kLinear) {
- DCHECK(a->induction_class == kWrapAround || a->induction_class == kPeriodic);
+ if (a->induction_class == kWrapAround || a->induction_class == kPeriodic) {
new_a = TransferAddSub(new_a, b, op);
}
- return CreateInduction(a->induction_class, new_a, new_b, type_);
+ return CreateInduction(a->induction_class, a->operation, new_a, new_b, a->fetch, type_);
+ }
+ }
+ return nullptr;
+}
+
+HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferNeg(InductionInfo* a) {
+ // Transfer over a unary negation: an invariant, linear, polynomial, geometric (mul),
+ // wrap-around, or periodic input yields a similar but negated induction as result.
+ if (a != nullptr) {
+ type_ = Narrowest(type_, a->type);
+ if (a->induction_class == kInvariant) {
+ return CreateInvariantOp(kNeg, nullptr, a);
+ } else if (a->induction_class != kGeometric || a->operation == kMul) {
+ return CreateInduction(a->induction_class,
+ a->operation,
+ TransferNeg(a->op_a),
+ TransferNeg(a->op_b),
+ a->fetch,
+ type_);
}
}
return nullptr;
@@ -403,72 +456,45 @@
HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferMul(InductionInfo* a,
InductionInfo* b) {
- // Transfer over a multiplication: any invariant, linear, wrap-around, or periodic
- // can be multiplied with an invariant to yield a similar but multiplied result.
- // Two non-invariant inputs cannot be multiplied, however.
+ // Transfer over a multiplication: any invariant, linear, polynomial, geometric (mul),
+ // wrap-around, or periodic can be multiplied with an invariant to yield a similar
+ // but multiplied result. Two non-invariant inputs cannot be multiplied, however.
if (a != nullptr && b != nullptr) {
type_ = Narrowest(type_, Narrowest(a->type, b->type));
if (a->induction_class == kInvariant && b->induction_class == kInvariant) {
return CreateInvariantOp(kMul, a, b);
- } else if (a->induction_class == kInvariant) {
+ } else if (a->induction_class == kInvariant && (b->induction_class != kGeometric ||
+ b->operation == kMul)) {
return CreateInduction(b->induction_class,
+ b->operation,
TransferMul(a, b->op_a),
TransferMul(a, b->op_b),
+ b->fetch,
type_);
- } else if (b->induction_class == kInvariant) {
+ } else if (b->induction_class == kInvariant && (a->induction_class != kGeometric ||
+ a->operation == kMul)) {
return CreateInduction(a->induction_class,
+ a->operation,
TransferMul(a->op_a, b),
TransferMul(a->op_b, b),
+ a->fetch,
type_);
}
}
return nullptr;
}
-HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferShl(InductionInfo* a,
- InductionInfo* b,
- Primitive::Type type) {
- // Transfer over a shift left: treat shift by restricted constant as equivalent multiplication.
- int64_t value = -1;
- if (a != nullptr && IsExact(b, &value)) {
- // Obtain the constant needed for the multiplication. This yields an existing instruction
- // if the constants is already there. Otherwise, this has a side effect on the HIR.
- // The restriction on the shift factor avoids generating a negative constant
- // (viz. 1 << 31 and 1L << 63 set the sign bit). The code assumes that generalization
- // for shift factors outside [0,32) and [0,64) ranges is done by earlier simplification.
- if ((type == Primitive::kPrimInt && 0 <= value && value < 31) ||
- (type == Primitive::kPrimLong && 0 <= value && value < 63)) {
- return TransferMul(a, CreateConstant(1 << value, type));
- }
- }
- return nullptr;
-}
-
-HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferNeg(InductionInfo* a) {
- // Transfer over a unary negation: an invariant, linear, wrap-around, or periodic input
- // yields a similar but negated induction as result.
- if (a != nullptr) {
- type_ = Narrowest(type_, a->type);
- if (a->induction_class == kInvariant) {
- return CreateInvariantOp(kNeg, nullptr, a);
- }
- return CreateInduction(a->induction_class, TransferNeg(a->op_a), TransferNeg(a->op_b), type_);
- }
- return nullptr;
-}
-
HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferCnv(InductionInfo* a,
Primitive::Type from,
Primitive::Type to) {
if (a != nullptr) {
- // Allow narrowing conversion in certain cases.
+ // Allow narrowing conversion on linear induction in certain cases.
if (IsNarrowingIntegralConversion(from, to)) {
if (a->induction_class == kLinear) {
if (a->type == to || (a->type == from && IsNarrowingIntegralConversion(from, to))) {
- return CreateInduction(kLinear, a->op_a, a->op_b, to);
+ return CreateInduction(kLinear, kNop, a->op_a, a->op_b, /*fetch*/ nullptr, to);
}
}
- // TODO: other cases useful too?
}
}
return nullptr;
@@ -511,11 +537,11 @@
if (a != nullptr && a->induction_class == kInvariant) {
if (phi->InputAt(1) == entry_phi) {
InductionInfo* initial = LookupInfo(loop, entry_phi->InputAt(0));
- return CreateInduction(kPeriodic, a, initial, type_);
+ return CreateInduction(kPeriodic, kNop, a, initial, /*fetch*/ nullptr, type_);
}
InductionInfo* b = SolvePhi(phi, /* input_index */ 1);
if (b != nullptr && b->induction_class == kPeriodic) {
- return CreateInduction(kPeriodic, a, b, type_);
+ return CreateInduction(kPeriodic, kNop, a, b, /*fetch*/ nullptr, type_);
}
}
}
@@ -530,7 +556,7 @@
InductionOp op,
bool is_first_call) {
// Solve within a cycle over an addition or subtraction: adding or subtracting an
- // invariant value, seeded from phi, keeps adding to the stride of the induction.
+ // invariant value, seeded from phi, keeps adding to the stride of the linear induction.
InductionInfo* b = LookupInfo(loop, y);
if (b != nullptr && b->induction_class == kInvariant) {
if (x == entry_phi) {
@@ -553,12 +579,17 @@
}
} else if (op == kSub) {
// Solve within a tight cycle that is formed by exactly two instructions,
- // one phi and one update, for a periodic idiom of the form k = c - k;
+ // one phi and one update, for a periodic idiom of the form k = c - k.
if (y == entry_phi && entry_phi->InputCount() == 2 && instruction == entry_phi->InputAt(1)) {
InductionInfo* a = LookupInfo(loop, x);
if (a != nullptr && a->induction_class == kInvariant) {
InductionInfo* initial = LookupInfo(loop, entry_phi->InputAt(0));
- return CreateInduction(kPeriodic, CreateInvariantOp(kSub, a, initial), initial, type_);
+ return CreateInduction(kPeriodic,
+ kNop,
+ CreateInvariantOp(kSub, a, initial),
+ initial,
+ /*fetch*/ nullptr,
+ type_);
}
}
}
@@ -566,21 +597,54 @@
return nullptr;
}
+HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::SolveGeo(HLoopInformation* loop,
+ HInstruction* entry_phi,
+ HInstruction* instruction,
+ HInstruction* x,
+ HInstruction* y,
+ InductionOp op) {
+ // Solve within a tight cycle that is formed by exactly two instructions, one phi and
+ // one update, for a geometric induction of the form k = k * c.
+ if (x == entry_phi && entry_phi->InputCount() == 2 && instruction == entry_phi->InputAt(1)) {
+ InductionInfo* initial = LookupInfo(loop, entry_phi->InputAt(0));
+ InductionInfo* b = LookupInfo(loop, y);
+ if (b != nullptr && b->induction_class == kInvariant && b->operation == kFetch) {
+ return CreateInduction(kGeometric,
+ op,
+ initial,
+ CreateConstant(0, type_),
+ b->fetch,
+ type_);
+ }
+ }
+ return nullptr;
+}
+
HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::SolveXor(HLoopInformation* loop,
HInstruction* entry_phi,
HInstruction* instruction,
HInstruction* x,
HInstruction* y) {
- // Solve within a tight cycle on x = c ^ x or x = x ^ c.
+ // Solve within a tight cycle on periodic idiom of the form x = c ^ x or x = x ^ c.
if (entry_phi->InputCount() == 2 && instruction == entry_phi->InputAt(1)) {
InductionInfo* initial = LookupInfo(loop, entry_phi->InputAt(0));
InductionInfo* a = LookupInfo(loop, x);
if (a != nullptr && a->induction_class == kInvariant && entry_phi == y) {
- return CreateInduction(kPeriodic, CreateInvariantOp(kXor, a, initial), initial, type_);
+ return CreateInduction(kPeriodic,
+ kNop,
+ CreateInvariantOp(kXor, a, initial),
+ initial,
+ /*fetch*/ nullptr,
+ type_);
}
InductionInfo* b = LookupInfo(loop, y);
if (b != nullptr && b->induction_class == kInvariant && entry_phi == x) {
- return CreateInduction(kPeriodic, CreateInvariantOp(kXor, initial, b), initial, type_);
+ return CreateInduction(kPeriodic,
+ kNop,
+ CreateInvariantOp(kXor, initial, b),
+ initial,
+ /*fetch*/ nullptr,
+ type_);
}
}
return nullptr;
@@ -590,7 +654,7 @@
HInstruction* entry_phi,
HInstruction* instruction,
int64_t opposite_value) {
- // Detect hidden XOR construction in tight cycles on x = (x == 0) or x = (x != 1).
+ // Detect hidden XOR construction in x = (x == false) or x = (x != true).
int64_t value = -1;
HInstruction* x = instruction->InputAt(0);
HInstruction* y = instruction->InputAt(1);
@@ -881,11 +945,14 @@
HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::CreateConstant(int64_t value,
Primitive::Type type) {
- if (type == Primitive::kPrimInt) {
- return CreateInvariantFetch(graph_->GetIntConstant(value));
+ HInstruction* constant;
+ switch (type) {
+ case Primitive::kPrimDouble: constant = graph_->GetDoubleConstant(value); break;
+ case Primitive::kPrimFloat: constant = graph_->GetFloatConstant(value); break;
+ case Primitive::kPrimLong: constant = graph_->GetLongConstant(value); break;
+ default: constant = graph_->GetIntConstant(value); break;
}
- DCHECK_EQ(type, Primitive::kPrimLong);
- return CreateInvariantFetch(graph_->GetLongConstant(value));
+ return CreateInvariantFetch(constant);
}
HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::CreateSimplifiedInvariant(
@@ -948,6 +1015,26 @@
return new (graph_->GetArena()) InductionInfo(kInvariant, op, a, b, nullptr, b->type);
}
+HInstruction* HInductionVarAnalysis::GetMultConstantForShift(HLoopInformation* loop,
+ HInstruction* instruction) {
+ // Obtain the constant needed to treat shift as equivalent multiplication. This yields an
+ // existing instruction if the constants is already there. Otherwise, this has a side effect
+ // on the HIR. The restriction on the shift factor avoids generating a negative constant
+ // (viz. 1 << 31 and 1L << 63 set the sign bit). The code assumes that generalization for
+ // shift factors outside [0,32) and [0,64) ranges is done by earlier simplification.
+ InductionInfo* b = LookupInfo(loop, instruction->InputAt(1));
+ int64_t value = -1;
+ if (IsExact(b, &value)) {
+ Primitive::Type type = instruction->InputAt(0)->GetType();
+ if (type == Primitive::kPrimInt && 0 <= value && value < 31) {
+ return graph_->GetIntConstant(1 << value);
+ }
+ if (type == Primitive::kPrimLong && 0 <= value && value < 63) {
+ return graph_->GetLongConstant(1L << value);
+ }
+ }
+ return nullptr;
+}
void HInductionVarAnalysis::AssignCycle(HPhi* phi) {
ArenaSet<HInstruction*>* set = &cycles_.Put(phi, ArenaSet<HInstruction*>(
@@ -993,6 +1080,16 @@
return info1 == info2;
}
+std::string HInductionVarAnalysis::FetchToString(HInstruction* fetch) {
+ DCHECK(fetch != nullptr);
+ if (fetch->IsIntConstant()) {
+ return std::to_string(fetch->AsIntConstant()->GetValue());
+ } else if (fetch->IsLongConstant()) {
+ return std::to_string(fetch->AsLongConstant()->GetValue());
+ }
+ return std::to_string(fetch->GetId()) + ":" + fetch->DebugName();
+}
+
std::string HInductionVarAnalysis::InductionToString(InductionInfo* info) {
if (info != nullptr) {
if (info->induction_class == kInvariant) {
@@ -1010,16 +1107,7 @@
case kLE: inv += " <= "; break;
case kGT: inv += " > "; break;
case kGE: inv += " >= "; break;
- case kFetch:
- DCHECK(info->fetch);
- if (info->fetch->IsIntConstant()) {
- inv += std::to_string(info->fetch->AsIntConstant()->GetValue());
- } else if (info->fetch->IsLongConstant()) {
- inv += std::to_string(info->fetch->AsLongConstant()->GetValue());
- } else {
- inv += std::to_string(info->fetch->GetId()) + ":" + info->fetch->DebugName();
- }
- break;
+ case kFetch: inv += FetchToString(info->fetch); break;
case kTripCountInLoop: inv += " (TC-loop) "; break;
case kTripCountInBody: inv += " (TC-body) "; break;
case kTripCountInLoopUnsafe: inv += " (TC-loop-unsafe) "; break;
@@ -1029,11 +1117,28 @@
inv += ")";
return inv;
} else {
- DCHECK(info->operation == kNop);
if (info->induction_class == kLinear) {
return "(" + InductionToString(info->op_a) + " * i + " +
InductionToString(info->op_b) + "):" +
Primitive::PrettyDescriptor(info->type);
+ } else if (info->induction_class == kPolynomial) {
+ return "poly( sum_i(" + InductionToString(info->op_a) + ") + " +
+ InductionToString(info->op_b) + "):" +
+ Primitive::PrettyDescriptor(info->type);
+ } else if (info->induction_class == kGeometric) {
+ DCHECK(info->fetch != nullptr);
+ if (info->operation == kNop) {
+ return "geo(" + InductionToString(info->op_a) + " mod_i " +
+ FetchToString(info->fetch) + " + " +
+ InductionToString(info->op_b) + "):" +
+ Primitive::PrettyDescriptor(info->type);
+ } else {
+ return "geo(" + InductionToString(info->op_a) + " * " +
+ FetchToString(info->fetch) +
+ (info->operation == kMul ? " ^ i + " : " ^ -i + ") +
+ InductionToString(info->op_b) + "):" +
+ Primitive::PrettyDescriptor(info->type);
+ }
} else if (info->induction_class == kWrapAround) {
return "wrap(" + InductionToString(info->op_a) + ", " +
InductionToString(info->op_b) + "):" +
diff --git a/compiler/optimizing/induction_var_analysis.h b/compiler/optimizing/induction_var_analysis.h
index 7027179..94afc71 100644
--- a/compiler/optimizing/induction_var_analysis.h
+++ b/compiler/optimizing/induction_var_analysis.h
@@ -51,14 +51,15 @@
enum InductionClass {
kInvariant,
kLinear,
+ kPolynomial,
+ kGeometric,
kWrapAround,
kPeriodic
};
enum InductionOp {
- // No-operation: a true induction.
+ // Operations.
kNop,
- // Various invariant operations.
kAdd,
kSub,
kNeg,
@@ -81,16 +82,18 @@
/**
* Defines a detected induction as:
* (1) invariant:
- * operation: a + b, a - b, -b, a * b, a / b
- * or:
- * fetch: fetch from HIR
+ * op: a + b, a - b, -b, a * b, a / b, a % b, a ^ b, fetch
* (2) linear:
* nop: a * i + b
- * (3) wrap-around
+ * (3) polynomial: // TODO: coming soon
+ * nop: sum_i(a) + b, for linear a
+ * (4) geometric:
+ * op: a * fetch^i + b, a * fetch^-i + b, a mod_i fetch + b
+ * (5) wrap-around
* nop: a, then defined by b
- * (4) periodic
+ * (6) periodic
* nop: a, then defined by b (repeated when exhausted)
- * (5) trip-count:
+ * (7) trip-count:
* tc: defined by a, taken-test in b
*/
struct InductionInfo : public ArenaObject<kArenaAllocInductionVarAnalysis> {
@@ -138,11 +141,13 @@
}
InductionInfo* CreateInduction(InductionClass ic,
+ InductionOp op,
InductionInfo* a,
InductionInfo* b,
+ HInstruction* f,
Primitive::Type type) {
DCHECK(a != nullptr && b != nullptr);
- return new (graph_->GetArena()) InductionInfo(ic, kNop, a, b, nullptr, type);
+ return new (graph_->GetArena()) InductionInfo(ic, op, a, b, f, type);
}
// Methods for analysis.
@@ -156,9 +161,8 @@
// Transfer operations.
InductionInfo* TransferPhi(HLoopInformation* loop, HInstruction* phi, size_t input_index);
InductionInfo* TransferAddSub(InductionInfo* a, InductionInfo* b, InductionOp op);
- InductionInfo* TransferMul(InductionInfo* a, InductionInfo* b);
- InductionInfo* TransferShl(InductionInfo* a, InductionInfo* b, Primitive::Type type);
InductionInfo* TransferNeg(InductionInfo* a);
+ InductionInfo* TransferMul(InductionInfo* a, InductionInfo* b);
InductionInfo* TransferCnv(InductionInfo* a, Primitive::Type from, Primitive::Type to);
// Solvers.
@@ -173,6 +177,12 @@
HInstruction* y,
InductionOp op,
bool is_first_call); // possibly swaps x and y to try again
+ InductionInfo* SolveGeo(HLoopInformation* loop,
+ HInstruction* entry_phi,
+ HInstruction* instruction,
+ HInstruction* x,
+ HInstruction* y,
+ InductionOp op);
InductionInfo* SolveXor(HLoopInformation* loop,
HInstruction* entry_phi,
HInstruction* instruction,
@@ -214,6 +224,7 @@
InductionInfo* LookupInfo(HLoopInformation* loop, HInstruction* instruction);
InductionInfo* CreateConstant(int64_t value, Primitive::Type type);
InductionInfo* CreateSimplifiedInvariant(InductionOp op, InductionInfo* a, InductionInfo* b);
+ HInstruction* GetMultConstantForShift(HLoopInformation* loop, HInstruction* instruction);
void AssignCycle(HPhi* phi);
ArenaSet<HInstruction*>* LookupCycle(HPhi* phi);
@@ -224,6 +235,7 @@
// Helpers.
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.
diff --git a/compiler/optimizing/induction_var_analysis_test.cc b/compiler/optimizing/induction_var_analysis_test.cc
index 3425b88..2199c8e 100644
--- a/compiler/optimizing/induction_var_analysis_test.cc
+++ b/compiler/optimizing/induction_var_analysis_test.cc
@@ -84,6 +84,7 @@
entry_->AddInstruction(parameter_);
constant0_ = graph_->GetIntConstant(0);
constant1_ = graph_->GetIntConstant(1);
+ constant2_ = graph_->GetIntConstant(2);
constant100_ = graph_->GetIntConstant(100);
float_constant0_ = graph_->GetFloatConstant(0.0f);
return_->AddInstruction(new (&allocator_) HReturnVoid());
@@ -191,6 +192,7 @@
HInstruction* parameter_; // "this"
HInstruction* constant0_;
HInstruction* constant1_;
+ HInstruction* constant2_;
HInstruction* constant100_;
HInstruction* float_constant0_;
@@ -252,11 +254,11 @@
TEST_F(InductionVarAnalysisTest, FindDerivedInduction) {
// Setup:
// for (int i = 0; i < 100; i++) {
- // k = 100 + i;
- // k = 100 - i;
- // k = 100 * i;
- // k = i << 1;
- // k = - i;
+ // t = 100 + i;
+ // t = 100 - i;
+ // t = 100 * i;
+ // t = i << 1;
+ // t = - i;
// }
BuildLoopNest(1);
HInstruction* add = InsertInstruction(
@@ -288,18 +290,20 @@
// a[k] = 0;
// }
BuildLoopNest(1);
- HPhi* k = InsertLoopPhi(0, 0);
- k->AddInput(constant0_);
+ HPhi* k_header = InsertLoopPhi(0, 0);
+ k_header->AddInput(constant0_);
HInstruction* add = InsertInstruction(
- new (&allocator_) HAdd(Primitive::kPrimInt, k, constant100_), 0);
+ new (&allocator_) HAdd(Primitive::kPrimInt, k_header, constant100_), 0);
HInstruction* store1 = InsertArrayStore(add, 0);
HInstruction* sub = InsertInstruction(
new (&allocator_) HSub(Primitive::kPrimInt, add, constant1_), 0);
HInstruction* store2 = InsertArrayStore(sub, 0);
- k->AddInput(sub);
+ k_header->AddInput(sub);
PerformInductionVarAnalysis();
+ EXPECT_STREQ("(((100) - (1)) * i + (0)):PrimInt",
+ GetInductionInfo(k_header, 0).c_str());
EXPECT_STREQ("(((100) - (1)) * i + (100)):PrimInt",
GetInductionInfo(store1->InputAt(1), 0).c_str());
EXPECT_STREQ("(((100) - (1)) * i + ((100) - (1))):PrimInt",
@@ -335,6 +339,7 @@
k_header->AddInput(k_body);
PerformInductionVarAnalysis();
+ EXPECT_STREQ("((1) * i + (0)):PrimInt", GetInductionInfo(k_header, 0).c_str());
EXPECT_STREQ("((1) * i + (1)):PrimInt", GetInductionInfo(store->InputAt(1), 0).c_str());
// Both increments get same induction.
@@ -367,6 +372,153 @@
PerformInductionVarAnalysis();
EXPECT_STREQ("((1) * i + (1)):PrimInt", GetInductionInfo(store->InputAt(1), 0).c_str());
+
+ // Both increments get same induction.
+ EXPECT_TRUE(HaveSameInduction(store->InputAt(1), inc1));
+ EXPECT_TRUE(HaveSameInduction(store->InputAt(1), inc2));
+}
+
+TEST_F(InductionVarAnalysisTest, FindGeometricMulInduction) {
+ // Setup:
+ // k = 1;
+ // for (int i = 0; i < 100; i++) {
+ // k = k * 100; // geometric (x 100)
+ // }
+ BuildLoopNest(1);
+ HPhi* k_header = InsertLoopPhi(0, 0);
+ k_header->AddInput(constant1_);
+
+ HInstruction* mul = InsertInstruction(
+ new (&allocator_) HMul(Primitive::kPrimInt, k_header, constant100_), 0);
+ k_header->AddInput(mul);
+ PerformInductionVarAnalysis();
+
+ EXPECT_STREQ("geo((1) * 100 ^ i + (0)):PrimInt", GetInductionInfo(k_header, 0).c_str());
+ EXPECT_STREQ("geo((100) * 100 ^ i + (0)):PrimInt", GetInductionInfo(mul, 0).c_str());
+}
+
+TEST_F(InductionVarAnalysisTest, FindGeometricShlInductionAndDerived) {
+ // Setup:
+ // k = 1;
+ // for (int i = 0; i < 100; i++) {
+ // t = k + 1;
+ // k = k << 1; // geometric (x 2)
+ // t = k + 100;
+ // t = k - 1;
+ // t = - t;
+ // t = k * 2;
+ // t = k << 2;
+ // }
+ BuildLoopNest(1);
+ HPhi* k_header = InsertLoopPhi(0, 0);
+ k_header->AddInput(constant1_);
+
+ HInstruction* add1 = InsertInstruction(
+ new (&allocator_) HAdd(Primitive::kPrimInt, k_header, constant1_), 0);
+ HInstruction* shl1 = InsertInstruction(
+ new (&allocator_) HShl(Primitive::kPrimInt, k_header, constant1_), 0);
+ HInstruction* add2 = InsertInstruction(
+ new (&allocator_) HAdd(Primitive::kPrimInt, shl1, constant100_), 0);
+ HInstruction* sub = InsertInstruction(
+ new (&allocator_) HSub(Primitive::kPrimInt, shl1, constant1_), 0);
+ HInstruction* neg = InsertInstruction(
+ new (&allocator_) HNeg(Primitive::kPrimInt, sub), 0);
+ HInstruction* mul = InsertInstruction(
+ new (&allocator_) HMul(Primitive::kPrimInt, shl1, constant2_), 0);
+ HInstruction* shl2 = InsertInstruction(
+ new (&allocator_) HShl(Primitive::kPrimInt, shl1, constant2_), 0);
+ k_header->AddInput(shl1);
+ PerformInductionVarAnalysis();
+
+ EXPECT_STREQ("geo((1) * 2 ^ i + (0)):PrimInt", GetInductionInfo(k_header, 0).c_str());
+ EXPECT_STREQ("geo((1) * 2 ^ i + (1)):PrimInt", GetInductionInfo(add1, 0).c_str());
+ EXPECT_STREQ("geo((2) * 2 ^ i + (0)):PrimInt", GetInductionInfo(shl1, 0).c_str());
+ EXPECT_STREQ("geo((2) * 2 ^ i + (100)):PrimInt", GetInductionInfo(add2, 0).c_str());
+ EXPECT_STREQ("geo((2) * 2 ^ i + ((0) - (1))):PrimInt", GetInductionInfo(sub, 0).c_str());
+ EXPECT_STREQ("geo(( - (2)) * 2 ^ i + ( - ((0) - (1)))):PrimInt",
+ GetInductionInfo(neg, 0).c_str());
+ EXPECT_STREQ("geo(((2) * (2)) * 2 ^ i + (0)):PrimInt", GetInductionInfo(mul, 0).c_str());
+ EXPECT_STREQ("geo(((2) * (4)) * 2 ^ i + (0)):PrimInt", GetInductionInfo(shl2, 0).c_str());
+}
+
+TEST_F(InductionVarAnalysisTest, FindGeometricDivInductionAndDerived) {
+ // Setup:
+ // k = 1;
+ // for (int i = 0; i < 100; i++) {
+ // t = k + 100;
+ // t = k - 1;
+ // t = - t
+ // t = k * 2;
+ // t = k << 2;
+ // k = k / 100; // geometric (/ 100)
+ // }
+ BuildLoopNest(1);
+ HPhi* k_header = InsertLoopPhi(0, 0);
+ k_header->AddInput(constant1_);
+
+ HInstruction* add = InsertInstruction(
+ new (&allocator_) HAdd(Primitive::kPrimInt, k_header, constant100_), 0);
+ HInstruction* sub = InsertInstruction(
+ new (&allocator_) HSub(Primitive::kPrimInt, k_header, constant1_), 0);
+ HInstruction* neg = InsertInstruction(
+ new (&allocator_) HNeg(Primitive::kPrimInt, sub), 0);
+ HInstruction* mul = InsertInstruction(
+ new (&allocator_) HMul(Primitive::kPrimInt, k_header, constant2_), 0);
+ HInstruction* shl = InsertInstruction(
+ new (&allocator_) HShl(Primitive::kPrimInt, k_header, constant2_), 0);
+ HInstruction* div = InsertInstruction(
+ new (&allocator_) HDiv(Primitive::kPrimInt, k_header, constant100_, kNoDexPc), 0);
+ k_header->AddInput(div);
+ PerformInductionVarAnalysis();
+
+ // Note, only the phi in the cycle and direct additive derived are classified.
+ EXPECT_STREQ("geo((1) * 100 ^ -i + (0)):PrimInt", GetInductionInfo(k_header, 0).c_str());
+ EXPECT_STREQ("geo((1) * 100 ^ -i + (100)):PrimInt", GetInductionInfo(add, 0).c_str());
+ EXPECT_STREQ("geo((1) * 100 ^ -i + ((0) - (1))):PrimInt", GetInductionInfo(sub, 0).c_str());
+ EXPECT_STREQ("", GetInductionInfo(neg, 0).c_str());
+ EXPECT_STREQ("", GetInductionInfo(mul, 0).c_str());
+ EXPECT_STREQ("", GetInductionInfo(shl, 0).c_str());
+ EXPECT_STREQ("", GetInductionInfo(div, 0).c_str());
+}
+
+TEST_F(InductionVarAnalysisTest, FindGeometricRemInductionAndDerived) {
+ // Setup:
+ // k = 1;
+ // for (int i = 0; i < 100; i++) {
+ // t = k + 100;
+ // t = k - 1;
+ // t = -t
+ // t = k * 2;
+ // t = k << 2;
+ // k = k % 100; // geometric (% 100)
+ // }
+ BuildLoopNest(1);
+ HPhi* k_header = InsertLoopPhi(0, 0);
+ k_header->AddInput(constant1_);
+
+ HInstruction* add = InsertInstruction(
+ new (&allocator_) HAdd(Primitive::kPrimInt, k_header, constant100_), 0);
+ HInstruction* sub = InsertInstruction(
+ new (&allocator_) HSub(Primitive::kPrimInt, k_header, constant1_), 0);
+ HInstruction* neg = InsertInstruction(
+ new (&allocator_) HNeg(Primitive::kPrimInt, sub), 0);
+ HInstruction* mul = InsertInstruction(
+ new (&allocator_) HMul(Primitive::kPrimInt, k_header, constant2_), 0);
+ HInstruction* shl = InsertInstruction(
+ new (&allocator_) HShl(Primitive::kPrimInt, k_header, constant2_), 0);
+ HInstruction* rem = InsertInstruction(
+ new (&allocator_) HRem(Primitive::kPrimInt, k_header, constant100_, kNoDexPc), 0);
+ k_header->AddInput(rem);
+ PerformInductionVarAnalysis();
+
+ // Note, only the phi in the cycle and direct additive derived are classified.
+ EXPECT_STREQ("geo((1) mod_i 100 + (0)):PrimInt", GetInductionInfo(k_header, 0).c_str());
+ EXPECT_STREQ("geo((1) mod_i 100 + (100)):PrimInt", GetInductionInfo(add, 0).c_str());
+ EXPECT_STREQ("geo((1) mod_i 100 + ((0) - (1))):PrimInt", GetInductionInfo(sub, 0).c_str());
+ EXPECT_STREQ("", GetInductionInfo(neg, 0).c_str());
+ EXPECT_STREQ("", GetInductionInfo(mul, 0).c_str());
+ EXPECT_STREQ("", GetInductionInfo(shl, 0).c_str());
+ EXPECT_STREQ("", GetInductionInfo(rem, 0).c_str());
}
TEST_F(InductionVarAnalysisTest, FindFirstOrderWrapAroundInduction) {
@@ -377,17 +529,20 @@
// k = 100 - i;
// }
BuildLoopNest(1);
- HPhi* k = InsertLoopPhi(0, 0);
- k->AddInput(constant0_);
+ HPhi* k_header = InsertLoopPhi(0, 0);
+ k_header->AddInput(constant0_);
- HInstruction* store = InsertArrayStore(k, 0);
+ HInstruction* store = InsertArrayStore(k_header, 0);
HInstruction* sub = InsertInstruction(
new (&allocator_) HSub(Primitive::kPrimInt, constant100_, basic_[0]), 0);
- k->AddInput(sub);
+ k_header->AddInput(sub);
PerformInductionVarAnalysis();
EXPECT_STREQ("wrap((0), (( - (1)) * i + (100)):PrimInt):PrimInt",
+ GetInductionInfo(k_header, 0).c_str());
+ EXPECT_STREQ("wrap((0), (( - (1)) * i + (100)):PrimInt):PrimInt",
GetInductionInfo(store->InputAt(1), 0).c_str());
+ EXPECT_STREQ("(( - (1)) * i + (100)):PrimInt", GetInductionInfo(sub, 0).c_str());
}
TEST_F(InductionVarAnalysisTest, FindSecondOrderWrapAroundInduction) {
@@ -400,13 +555,13 @@
// t = 100 - i;
// }
BuildLoopNest(1);
- HPhi* k = InsertLoopPhi(0, 0);
- k->AddInput(constant0_);
+ HPhi* k_header = InsertLoopPhi(0, 0);
+ k_header->AddInput(constant0_);
HPhi* t = InsertLoopPhi(1, 0);
t->AddInput(constant100_);
- HInstruction* store = InsertArrayStore(k, 0);
- k->AddInput(t);
+ HInstruction* store = InsertArrayStore(k_header, 0);
+ k_header->AddInput(t);
HInstruction* sub = InsertInstruction(
new (&allocator_) HSub(Primitive::kPrimInt, constant100_, basic_[0], 0), 0);
t->AddInput(sub);
@@ -426,23 +581,27 @@
// t = k << 1;
// t = - k;
// k = i << 1;
+ // t = - k;
// }
BuildLoopNest(1);
- HPhi* k = InsertLoopPhi(0, 0);
- k->AddInput(constant0_);
+ HPhi* k_header = InsertLoopPhi(0, 0);
+ k_header->AddInput(constant0_);
HInstruction* add = InsertInstruction(
- new (&allocator_) HAdd(Primitive::kPrimInt, k, constant100_), 0);
+ new (&allocator_) HAdd(Primitive::kPrimInt, k_header, constant100_), 0);
HInstruction* sub = InsertInstruction(
- new (&allocator_) HSub(Primitive::kPrimInt, k, constant100_), 0);
+ new (&allocator_) HSub(Primitive::kPrimInt, k_header, constant100_), 0);
HInstruction* mul = InsertInstruction(
- new (&allocator_) HMul(Primitive::kPrimInt, k, constant100_), 0);
- HInstruction* shl = InsertInstruction(
- new (&allocator_) HShl(Primitive::kPrimInt, k, constant1_), 0);
- HInstruction* neg = InsertInstruction(
- new (&allocator_) HNeg(Primitive::kPrimInt, k), 0);
- k->AddInput(
- InsertInstruction(new (&allocator_) HShl(Primitive::kPrimInt, basic_[0], constant1_), 0));
+ new (&allocator_) HMul(Primitive::kPrimInt, k_header, constant100_), 0);
+ HInstruction* shl1 = InsertInstruction(
+ new (&allocator_) HShl(Primitive::kPrimInt, k_header, constant1_), 0);
+ HInstruction* neg1 = InsertInstruction(
+ new (&allocator_) HNeg(Primitive::kPrimInt, k_header), 0);
+ HInstruction* shl2 = InsertInstruction(
+ new (&allocator_) HShl(Primitive::kPrimInt, basic_[0], constant1_), 0);
+ HInstruction* neg2 = InsertInstruction(
+ new (&allocator_) HNeg(Primitive::kPrimInt, shl2), 0);
+ k_header->AddInput(shl2);
PerformInductionVarAnalysis();
EXPECT_STREQ("wrap((100), ((2) * i + (100)):PrimInt):PrimInt",
@@ -452,9 +611,11 @@
EXPECT_STREQ("wrap((0), (((2) * (100)) * i + (0)):PrimInt):PrimInt",
GetInductionInfo(mul, 0).c_str());
EXPECT_STREQ("wrap((0), (((2) * (2)) * i + (0)):PrimInt):PrimInt",
- GetInductionInfo(shl, 0).c_str());
+ GetInductionInfo(shl1, 0).c_str());
EXPECT_STREQ("wrap((0), (( - (2)) * i + (0)):PrimInt):PrimInt",
- GetInductionInfo(neg, 0).c_str());
+ GetInductionInfo(neg1, 0).c_str());
+ EXPECT_STREQ("((2) * i + (0)):PrimInt", GetInductionInfo(shl2, 0).c_str());
+ EXPECT_STREQ("(( - (2)) * i + (0)):PrimInt", GetInductionInfo(neg2, 0).c_str());
}
TEST_F(InductionVarAnalysisTest, FindPeriodicInduction) {
@@ -470,15 +631,15 @@
// k = d;
// }
BuildLoopNest(1);
- HPhi* k = InsertLoopPhi(0, 0);
- k->AddInput(constant0_);
+ HPhi* k_header = InsertLoopPhi(0, 0);
+ k_header->AddInput(constant0_);
HPhi* t = InsertLoopPhi(1, 0);
t->AddInput(constant100_);
- HInstruction* store1 = InsertArrayStore(k, 0);
+ HInstruction* store1 = InsertArrayStore(k_header, 0);
HInstruction* store2 = InsertArrayStore(t, 0);
- k->AddInput(t);
- t->AddInput(k);
+ k_header->AddInput(t);
+ t->AddInput(k_header);
PerformInductionVarAnalysis();
EXPECT_STREQ("periodic((0), (100)):PrimInt", GetInductionInfo(store1->InputAt(1), 0).c_str());
@@ -493,13 +654,13 @@
// k = 1 - k;
// }
BuildLoopNest(1);
- HPhi* k = InsertLoopPhi(0, 0);
- k->AddInput(constant0_);
+ HPhi* k_header = InsertLoopPhi(0, 0);
+ k_header->AddInput(constant0_);
- HInstruction* store = InsertArrayStore(k, 0);
+ HInstruction* store = InsertArrayStore(k_header, 0);
HInstruction* sub = InsertInstruction(
- new (&allocator_) HSub(Primitive::kPrimInt, constant1_, k), 0);
- k->AddInput(sub);
+ new (&allocator_) HSub(Primitive::kPrimInt, constant1_, k_header), 0);
+ k_header->AddInput(sub);
PerformInductionVarAnalysis();
EXPECT_STREQ("periodic((0), (1)):PrimInt", GetInductionInfo(store->InputAt(1), 0).c_str());
@@ -514,13 +675,13 @@
// k = k ^ 1;
// }
BuildLoopNest(1);
- HPhi* k = InsertLoopPhi(0, 0);
- k->AddInput(constant0_);
+ HPhi* k_header = InsertLoopPhi(0, 0);
+ k_header->AddInput(constant0_);
- HInstruction* store = InsertArrayStore(k, 0);
+ HInstruction* store = InsertArrayStore(k_header, 0);
HInstruction* x = InsertInstruction(
- new (&allocator_) HXor(Primitive::kPrimInt, k, constant1_), 0);
- k->AddInput(x);
+ new (&allocator_) HXor(Primitive::kPrimInt, k_header, constant1_), 0);
+ k_header->AddInput(x);
PerformInductionVarAnalysis();
EXPECT_STREQ("periodic((0), (1)):PrimInt", GetInductionInfo(store->InputAt(1), 0).c_str());
@@ -534,14 +695,15 @@
// k = 1 ^ k;
// }
BuildLoopNest(1);
- HPhi* k = InsertLoopPhi(0, 0);
- k->AddInput(constant1_);
+ HPhi* k_header = InsertLoopPhi(0, 0);
+ k_header->AddInput(constant1_);
HInstruction* x = InsertInstruction(
- new (&allocator_) HXor(Primitive::kPrimInt, constant1_, k), 0);
- k->AddInput(x);
+ new (&allocator_) HXor(Primitive::kPrimInt, constant1_, k_header), 0);
+ k_header->AddInput(x);
PerformInductionVarAnalysis();
+ EXPECT_STREQ("periodic((1), ((1) ^ (1))):PrimInt", GetInductionInfo(k_header, 0).c_str());
EXPECT_STREQ("periodic(((1) ^ (1)), (1)):PrimInt", GetInductionInfo(x, 0).c_str());
}
@@ -552,14 +714,15 @@
// k = k ^ 100;
// }
BuildLoopNest(1);
- HPhi* k = InsertLoopPhi(0, 0);
- k->AddInput(constant1_);
+ HPhi* k_header = InsertLoopPhi(0, 0);
+ k_header->AddInput(constant1_);
HInstruction* x = InsertInstruction(
- new (&allocator_) HXor(Primitive::kPrimInt, k, constant100_), 0);
- k->AddInput(x);
+ new (&allocator_) HXor(Primitive::kPrimInt, k_header, constant100_), 0);
+ k_header->AddInput(x);
PerformInductionVarAnalysis();
+ EXPECT_STREQ("periodic((1), ((1) ^ (100))):PrimInt", GetInductionInfo(k_header, 0).c_str());
EXPECT_STREQ("periodic(((1) ^ (100)), (1)):PrimInt", GetInductionInfo(x, 0).c_str());
}
@@ -570,13 +733,14 @@
// k = (k == 0);
// }
BuildLoopNest(1);
- HPhi* k = InsertLoopPhi(0, 0);
- k->AddInput(constant0_);
+ HPhi* k_header = InsertLoopPhi(0, 0);
+ k_header->AddInput(constant0_);
- HInstruction* x = InsertInstruction(new (&allocator_) HEqual(k, constant0_), 0);
- k->AddInput(x);
+ HInstruction* x = InsertInstruction(new (&allocator_) HEqual(k_header, constant0_), 0);
+ k_header->AddInput(x);
PerformInductionVarAnalysis();
+ EXPECT_STREQ("periodic((0), (1)):PrimBoolean", GetInductionInfo(k_header, 0).c_str());
EXPECT_STREQ("periodic((1), (0)):PrimBoolean", GetInductionInfo(x, 0).c_str());
}
@@ -587,13 +751,14 @@
// k = (0 == k);
// }
BuildLoopNest(1);
- HPhi* k = InsertLoopPhi(0, 0);
- k->AddInput(constant0_);
+ HPhi* k_header = InsertLoopPhi(0, 0);
+ k_header->AddInput(constant0_);
- HInstruction* x = InsertInstruction(new (&allocator_) HEqual(constant0_, k), 0);
- k->AddInput(x);
+ HInstruction* x = InsertInstruction(new (&allocator_) HEqual(constant0_, k_header), 0);
+ k_header->AddInput(x);
PerformInductionVarAnalysis();
+ EXPECT_STREQ("periodic((0), (1)):PrimBoolean", GetInductionInfo(k_header, 0).c_str());
EXPECT_STREQ("periodic((1), (0)):PrimBoolean", GetInductionInfo(x, 0).c_str());
}
@@ -604,13 +769,14 @@
// k = (k != 1);
// }
BuildLoopNest(1);
- HPhi* k = InsertLoopPhi(0, 0);
- k->AddInput(constant0_);
+ HPhi* k_header = InsertLoopPhi(0, 0);
+ k_header->AddInput(constant0_);
- HInstruction* x = InsertInstruction(new (&allocator_) HNotEqual(k, constant1_), 0);
- k->AddInput(x);
+ HInstruction* x = InsertInstruction(new (&allocator_) HNotEqual(k_header, constant1_), 0);
+ k_header->AddInput(x);
PerformInductionVarAnalysis();
+ EXPECT_STREQ("periodic((0), (1)):PrimBoolean", GetInductionInfo(k_header, 0).c_str());
EXPECT_STREQ("periodic((1), (0)):PrimBoolean", GetInductionInfo(x, 0).c_str());
}
@@ -621,13 +787,14 @@
// k = (1 != k);
// }
BuildLoopNest(1);
- HPhi* k = InsertLoopPhi(0, 0);
- k->AddInput(constant0_);
+ HPhi* k_header = InsertLoopPhi(0, 0);
+ k_header->AddInput(constant0_);
- HInstruction* x = InsertInstruction(new (&allocator_) HNotEqual(constant1_, k), 0);
- k->AddInput(x);
+ HInstruction* x = InsertInstruction(new (&allocator_) HNotEqual(constant1_, k_header), 0);
+ k_header->AddInput(x);
PerformInductionVarAnalysis();
+ EXPECT_STREQ("periodic((0), (1)):PrimBoolean", GetInductionInfo(k_header, 0).c_str());
EXPECT_STREQ("periodic((1), (0)):PrimBoolean", GetInductionInfo(x, 0).c_str());
}
@@ -635,6 +802,7 @@
// Setup:
// k = 0;
// for (int i = 0; i < 100; i++) {
+ // t = - k;
// k = 1 - k;
// t = k + 100;
// t = k - 100;
@@ -646,28 +814,31 @@
HPhi* k_header = InsertLoopPhi(0, 0);
k_header->AddInput(constant0_);
- HInstruction* k_body = InsertInstruction(
+ HInstruction* neg1 = InsertInstruction(
+ new (&allocator_) HNeg(Primitive::kPrimInt, k_header), 0);
+ HInstruction* idiom = InsertInstruction(
new (&allocator_) HSub(Primitive::kPrimInt, constant1_, k_header), 0);
- k_header->AddInput(k_body);
-
- // Derived expressions.
HInstruction* add = InsertInstruction(
- new (&allocator_) HAdd(Primitive::kPrimInt, k_body, constant100_), 0);
+ new (&allocator_) HAdd(Primitive::kPrimInt, idiom, constant100_), 0);
HInstruction* sub = InsertInstruction(
- new (&allocator_) HSub(Primitive::kPrimInt, k_body, constant100_), 0);
+ new (&allocator_) HSub(Primitive::kPrimInt, idiom, constant100_), 0);
HInstruction* mul = InsertInstruction(
- new (&allocator_) HMul(Primitive::kPrimInt, k_body, constant100_), 0);
+ new (&allocator_) HMul(Primitive::kPrimInt, idiom, constant100_), 0);
HInstruction* shl = InsertInstruction(
- new (&allocator_) HShl(Primitive::kPrimInt, k_body, constant1_), 0);
- HInstruction* neg = InsertInstruction(
- new (&allocator_) HNeg(Primitive::kPrimInt, k_body), 0);
+ new (&allocator_) HShl(Primitive::kPrimInt, idiom, constant1_), 0);
+ HInstruction* neg2 = InsertInstruction(
+ new (&allocator_) HNeg(Primitive::kPrimInt, idiom), 0);
+ k_header->AddInput(idiom);
PerformInductionVarAnalysis();
+ EXPECT_STREQ("periodic((0), (1)):PrimInt", GetInductionInfo(k_header, 0).c_str());
+ EXPECT_STREQ("periodic((0), ( - (1))):PrimInt", GetInductionInfo(neg1, 0).c_str());
+ EXPECT_STREQ("periodic((1), (0)):PrimInt", GetInductionInfo(idiom, 0).c_str());
EXPECT_STREQ("periodic(((1) + (100)), (100)):PrimInt", GetInductionInfo(add, 0).c_str());
EXPECT_STREQ("periodic(((1) - (100)), ((0) - (100))):PrimInt", GetInductionInfo(sub, 0).c_str());
EXPECT_STREQ("periodic((100), (0)):PrimInt", GetInductionInfo(mul, 0).c_str());
EXPECT_STREQ("periodic((2), (0)):PrimInt", GetInductionInfo(shl, 0).c_str());
- EXPECT_STREQ("periodic(( - (1)), (0)):PrimInt", GetInductionInfo(neg, 0).c_str());
+ EXPECT_STREQ("periodic(( - (1)), (0)):PrimInt", GetInductionInfo(neg2, 0).c_str());
}
TEST_F(InductionVarAnalysisTest, FindDeepLoopInduction) {
@@ -683,18 +854,18 @@
// }
BuildLoopNest(10);
- HPhi* k[10];
+ HPhi* k_header[10];
for (int d = 0; d < 10; d++) {
- k[d] = InsertLoopPhi(0, d);
+ k_header[d] = InsertLoopPhi(0, d);
}
HInstruction* inc = InsertInstruction(
- new (&allocator_) HAdd(Primitive::kPrimInt, constant1_, k[9]), 9);
+ new (&allocator_) HAdd(Primitive::kPrimInt, constant1_, k_header[9]), 9);
HInstruction* store = InsertArrayStore(inc, 9);
for (int d = 0; d < 10; d++) {
- k[d]->AddInput((d != 0) ? k[d - 1] : constant0_);
- k[d]->AddInput((d != 9) ? k[d + 1] : inc);
+ k_header[d]->AddInput((d != 0) ? k_header[d - 1] : constant0_);
+ k_header[d]->AddInput((d != 9) ? k_header[d + 1] : inc);
}
PerformInductionVarAnalysis();
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;
diff --git a/compiler/optimizing/induction_var_range.h b/compiler/optimizing/induction_var_range.h
index 034cf32..f7360e8 100644
--- a/compiler/optimizing/induction_var_range.h
+++ b/compiler/optimizing/induction_var_range.h
@@ -190,6 +190,10 @@
HInductionVarAnalysis::InductionInfo* trip,
bool in_body,
bool is_min) const;
+ Value GetGeometric(HInductionVarAnalysis::InductionInfo* info,
+ HInductionVarAnalysis::InductionInfo* trip,
+ bool in_body,
+ bool is_min) const;
Value GetFetch(HInstruction* instruction,
HInductionVarAnalysis::InductionInfo* trip,
bool in_body,
@@ -245,6 +249,12 @@
/*out*/ bool* needs_finite_test,
/*out*/ bool* needs_taken_test) const;
+ bool GenerateLastValueGeometric(HInductionVarAnalysis::InductionInfo* info,
+ HInductionVarAnalysis::InductionInfo* trip,
+ HGraph* graph,
+ HBasicBlock* block,
+ /*out*/HInstruction** result) const;
+
bool GenerateLastValuePeriodic(HInductionVarAnalysis::InductionInfo* info,
HInductionVarAnalysis::InductionInfo* trip,
HGraph* graph,
diff --git a/compiler/optimizing/induction_var_range_test.cc b/compiler/optimizing/induction_var_range_test.cc
index 4c99e3c..510841e 100644
--- a/compiler/optimizing/induction_var_range_test.cc
+++ b/compiler/optimizing/induction_var_range_test.cc
@@ -170,22 +170,46 @@
/** Constructs a linear a * i + b induction. */
HInductionVarAnalysis::InductionInfo* CreateLinear(int32_t a, int32_t b) {
- return iva_->CreateInduction(
- HInductionVarAnalysis::kLinear, CreateConst(a), CreateConst(b), Primitive::kPrimInt);
+ return iva_->CreateInduction(HInductionVarAnalysis::kLinear,
+ HInductionVarAnalysis::kNop,
+ CreateConst(a),
+ CreateConst(b),
+ nullptr,
+ Primitive::kPrimInt);
+ }
+
+ /** Constructs a geometric a * f^i + b induction. */
+ HInductionVarAnalysis::InductionInfo* CreateGeometric(int32_t a, int32_t b, int32_t f, char op) {
+ return iva_->CreateInduction(HInductionVarAnalysis::kGeometric,
+ op == '*' ? HInductionVarAnalysis::kMul :
+ op == '/' ? HInductionVarAnalysis::kDiv :
+ HInductionVarAnalysis::kNop,
+ CreateConst(a),
+ CreateConst(b),
+ graph_->GetIntConstant(f),
+ Primitive::kPrimInt);
}
/** Constructs a range [lo, hi] using a periodic induction. */
HInductionVarAnalysis::InductionInfo* CreateRange(int32_t lo, int32_t hi) {
- return iva_->CreateInduction(
- HInductionVarAnalysis::kPeriodic, CreateConst(lo), CreateConst(hi), Primitive::kPrimInt);
+ return iva_->CreateInduction(HInductionVarAnalysis::kPeriodic,
+ HInductionVarAnalysis::kNop,
+ CreateConst(lo),
+ CreateConst(hi),
+ nullptr,
+ Primitive::kPrimInt);
}
/** Constructs a wrap-around induction consisting of a constant, followed info */
HInductionVarAnalysis::InductionInfo* CreateWrapAround(
int32_t initial,
HInductionVarAnalysis::InductionInfo* info) {
- return iva_->CreateInduction(
- HInductionVarAnalysis::kWrapAround, CreateConst(initial), info, Primitive::kPrimInt);
+ return iva_->CreateInduction(HInductionVarAnalysis::kWrapAround,
+ HInductionVarAnalysis::kNop,
+ CreateConst(initial),
+ info,
+ nullptr,
+ Primitive::kPrimInt);
}
/** Constructs a wrap-around induction consisting of a constant, followed by a range. */
@@ -414,6 +438,33 @@
ExpectEqual(Value(20), GetMax(CreateWrapAround(20, -1, 10), nullptr));
}
+TEST_F(InductionVarRangeTest, GetMinMaxGeometricMul) {
+ ExpectEqual(Value(), GetMin(CreateGeometric(1, 1, 1, '*'), nullptr));
+ ExpectEqual(Value(), GetMax(CreateGeometric(1, 1, 1, '*'), nullptr));
+}
+
+TEST_F(InductionVarRangeTest, GetMinMaxGeometricDiv) {
+ ExpectEqual(Value(5), GetMin(CreateGeometric(11, 5, 3, '/'), nullptr));
+ ExpectEqual(Value(16), GetMax(CreateGeometric(11, 5, 3, '/'), nullptr));
+ ExpectEqual(Value(-5), GetMin(CreateGeometric(11, -5, 3, '/'), nullptr));
+ ExpectEqual(Value(6), GetMax(CreateGeometric(11, -5, 3, '/'), nullptr));
+ ExpectEqual(Value(-6), GetMin(CreateGeometric(-11, 5, 3, '/'), nullptr));
+ ExpectEqual(Value(5), GetMax(CreateGeometric(-11, 5, 3, '/'), nullptr));
+ ExpectEqual(Value(-16), GetMin(CreateGeometric(-11, -5, 3, '/'), nullptr));
+ ExpectEqual(Value(-5), GetMax(CreateGeometric(-11, -5, 3, '/'), nullptr));
+}
+
+TEST_F(InductionVarRangeTest, GetMinMaxGeometricRem) {
+ ExpectEqual(Value(7), GetMin(CreateGeometric(11, 5, 3, '%'), nullptr));
+ ExpectEqual(Value(16), GetMax(CreateGeometric(11, 5, 3, '%'), nullptr));
+ ExpectEqual(Value(-3), GetMin(CreateGeometric(11, -5, 3, '%'), nullptr));
+ ExpectEqual(Value(6), GetMax(CreateGeometric(11, -5, 3, '%'), nullptr));
+ ExpectEqual(Value(-6), GetMin(CreateGeometric(-11, 5, 3, '%'), nullptr));
+ ExpectEqual(Value(3), GetMax(CreateGeometric(-11, 5, 3, '%'), nullptr));
+ ExpectEqual(Value(-16), GetMin(CreateGeometric(-11, -5, 3, '%'), nullptr));
+ ExpectEqual(Value(-7), GetMax(CreateGeometric(-11, -5, 3, '%'), nullptr));
+}
+
TEST_F(InductionVarRangeTest, GetMinMaxPeriodic) {
ExpectEqual(Value(-2), GetMin(CreateRange(-2, 99), nullptr));
ExpectEqual(Value(99), GetMax(CreateRange(-2, 99), nullptr));
diff --git a/test/530-checker-loops1/info.txt b/test/530-checker-loops1/info.txt
index f5d334d..ecefa7e 100644
--- a/test/530-checker-loops1/info.txt
+++ b/test/530-checker-loops1/info.txt
@@ -1 +1 @@
-Test on loop optimizations.
+Test on loop optimizations, in particular around common induction.
diff --git a/test/530-checker-loops1/src/Main.java b/test/530-checker-loops1/src/Main.java
index dde4d62..383c28f 100644
--- a/test/530-checker-loops1/src/Main.java
+++ b/test/530-checker-loops1/src/Main.java
@@ -15,7 +15,7 @@
*/
//
-// Test on loop optimizations.
+// Test on loop optimizations, in particular around common induction.
//
public class Main {
diff --git a/test/530-checker-loops2/info.txt b/test/530-checker-loops2/info.txt
index f5d334d..3b5a7ad 100644
--- a/test/530-checker-loops2/info.txt
+++ b/test/530-checker-loops2/info.txt
@@ -1 +1 @@
-Test on loop optimizations.
+Test on loop optimizations, in particular around less common induction.
diff --git a/test/530-checker-loops2/src/Main.java b/test/530-checker-loops2/src/Main.java
index 47b6475..a94d69b 100644
--- a/test/530-checker-loops2/src/Main.java
+++ b/test/530-checker-loops2/src/Main.java
@@ -15,7 +15,7 @@
*/
//
-// Test on loop optimizations.
+// Test on loop optimizations, in particular around less common induction.
//
public class Main {
diff --git a/test/530-checker-loops3/info.txt b/test/530-checker-loops3/info.txt
index 07d99a3..e262f8e 100644
--- a/test/530-checker-loops3/info.txt
+++ b/test/530-checker-loops3/info.txt
@@ -1 +1 @@
-Test on loop optimizations, in particular loop-based dynamic bce.
+Test on loop optimizations, in particular around loop-based dynamic bce.
diff --git a/test/530-checker-loops4/expected.txt b/test/530-checker-loops4/expected.txt
new file mode 100644
index 0000000..b0aad4d
--- /dev/null
+++ b/test/530-checker-loops4/expected.txt
@@ -0,0 +1 @@
+passed
diff --git a/test/530-checker-loops4/info.txt b/test/530-checker-loops4/info.txt
new file mode 100644
index 0000000..10cf3b1
--- /dev/null
+++ b/test/530-checker-loops4/info.txt
@@ -0,0 +1 @@
+Test on loop optimizations, in particular with geometric induction.
diff --git a/test/530-checker-loops4/src/Main.java b/test/530-checker-loops4/src/Main.java
new file mode 100644
index 0000000..2e19c88
--- /dev/null
+++ b/test/530-checker-loops4/src/Main.java
@@ -0,0 +1,323 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+//
+// Test on loop optimizations, in particular around geometric induction.
+//
+public class Main {
+
+ /// CHECK-START: int Main.geo1(int) loop_optimization (before)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>>
+ /// CHECK-DAG: Mul loop:<<Loop>>
+ //
+ /// CHECK-START: int Main.geo1(int) loop_optimization (after)
+ /// CHECK-DAG: <<Par:i\d+>> ParameterValue loop:none
+ /// CHECK-DAG: <<Zer:i\d+>> IntConstant 0 loop:none
+ /// CHECK-DAG: <<Int:i\d+>> IntConstant 1410065408 loop:none
+ /// CHECK-DAG: <<Mul:i\d+>> Mul [<<Par>>,<<Int>>] loop:none
+ /// CHECK-DAG: <<Add:i\d+>> Add [<<Mul>>,<<Zer>>] loop:none
+ /// CHECK-DAG: Return [<<Add>>] loop:none
+ //
+ /// CHECK-START: int Main.geo1(int) loop_optimization (after)
+ /// CHECK-NOT: Phi
+ public static int geo1(int a) {
+ for (int i = 0; i < 10; i++) {
+ a *= 10;
+ }
+ return a;
+ }
+
+ /// CHECK-START: int Main.geo2(int) loop_optimization (before)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>>
+ /// CHECK-DAG: Shl loop:<<Loop>>
+ //
+ /// CHECK-START: int Main.geo2(int) loop_optimization (after)
+ /// CHECK-DAG: <<Par:i\d+>> ParameterValue loop:none
+ /// CHECK-DAG: <<Zer:i\d+>> IntConstant 0 loop:none
+ /// CHECK-DAG: <<Int:i\d+>> IntConstant 1024 loop:none
+ /// CHECK-DAG: <<Mul:i\d+>> Mul [<<Par>>,<<Int>>] loop:none
+ /// CHECK-DAG: <<Add:i\d+>> Add [<<Mul>>,<<Zer>>] loop:none
+ /// CHECK-DAG: Return [<<Add>>] loop:none
+ //
+ /// CHECK-START: int Main.geo2(int) loop_optimization (after)
+ /// CHECK-NOT: Phi
+ public static int geo2(int a) {
+ for (int i = 0; i < 10; i++) {
+ a <<= 1;
+ }
+ return a;
+ }
+
+ /// CHECK-START: int Main.geo3(int) loop_optimization (before)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>>
+ /// CHECK-DAG: Div loop:<<Loop>>
+ //
+ /// CHECK-START: int Main.geo3(int) loop_optimization (after)
+ /// CHECK-DAG: <<Par:i\d+>> ParameterValue loop:none
+ /// CHECK-DAG: <<Zer:i\d+>> IntConstant 0 loop:none
+ /// CHECK-DAG: <<Int:i\d+>> IntConstant 59049 loop:none
+ /// CHECK-DAG: <<Div:i\d+>> Div [<<Par>>,<<Int>>] loop:none
+ /// CHECK-DAG: <<Add:i\d+>> Add [<<Div>>,<<Zer>>] loop:none
+ /// CHECK-DAG: Return [<<Add>>] loop:none
+ //
+ /// CHECK-START: int Main.geo3(int) loop_optimization (after)
+ /// CHECK-NOT: Phi
+ public static int geo3(int a) {
+ for (int i = 0; i < 10; i++) {
+ a /= 3;
+ }
+ return a;
+ }
+
+ /// CHECK-START: int Main.geo4(int) loop_optimization (before)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>>
+ /// CHECK-DAG: Rem loop:<<Loop>>
+ //
+ /// CHECK-START: int Main.geo4(int) loop_optimization (after)
+ /// CHECK-DAG: <<Par:i\d+>> ParameterValue loop:none
+ /// CHECK-DAG: <<Zer:i\d+>> IntConstant 0 loop:none
+ /// CHECK-DAG: <<Int:i\d+>> IntConstant 7 loop:none
+ /// CHECK-DAG: <<Rem:i\d+>> Rem [<<Par>>,<<Int>>] loop:none
+ /// CHECK-DAG: <<Add:i\d+>> Add [<<Rem>>,<<Zer>>] loop:none
+ /// CHECK-DAG: Return [<<Add>>] loop:none
+ //
+ /// CHECK-START: int Main.geo4(int) loop_optimization (after)
+ /// CHECK-NOT: Phi
+ public static int geo4(int a) {
+ for (int i = 0; i < 10; i++) {
+ a %= 7;
+ }
+ return a;
+ }
+
+ // TODO: someday?
+ public static int geo1BCE() {
+ int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+ 11, 12, 13, 14, 15, 16, 17, 19, 19, 20,
+ 21, 22, 23, 24, 25, 26 };
+ int a = 1;
+ int r = 0;
+ for (int i = 0; i < 3; i++) {
+ r += x[a];
+ a *= 5;
+ }
+ return r;
+ }
+
+ // TODO: someday?
+ public static int geo2BCE() {
+ int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+ 11, 12, 13, 14, 15, 16, 17, 19, 19, 20,
+ 21, 22, 23, 24, 25, 26 };
+ int a = 1;
+ int r = 0;
+ for (int i = 0; i < 5; i++) {
+ r += x[a];
+ a <<= 1;
+ }
+ return r;
+ }
+
+ /// CHECK-START: int Main.geo3BCE() BCE (before)
+ /// CHECK-DAG: BoundsCheck loop:none
+ /// CHECK-DAG: BoundsCheck loop:{{B\d+}}
+ //
+ /// CHECK-START: int Main.geo3BCE() BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK-NOT: Deoptimize
+ public static int geo3BCE() {
+ int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+ 11, 12, 13, 14, 15, 16, 17, 19, 19, 20,
+ 21, 22, 23, 24, 25, 26 };
+ int a = 25;
+ int r = 0;
+ for (int i = 0; i < 100; i++) { // a converges to 0
+ r += x[a];
+ a /= 5;
+ }
+ return r;
+ }
+
+ /// CHECK-START: int Main.geo4BCE() BCE (before)
+ /// CHECK-DAG: BoundsCheck loop:none
+ /// CHECK-DAG: BoundsCheck loop:{{B\d+}}
+ //
+ /// CHECK-START: int Main.geo4BCE() BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK-NOT: Deoptimize
+ public static int geo4BCE() {
+ int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+ 11, 12, 13, 14, 15, 16, 17, 19, 19, 20,
+ 21, 22, 23, 24, 25, 26 };
+ int a = 25;
+ int r = 0;
+ for (int i = 0; i < 100; i++) { // a converges to 0
+ r += x[a];
+ a %= 5;
+ }
+ return r;
+ }
+
+ /// CHECK-START: int Main.geoMulBlackHole(int) loop_optimization (before)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>>
+ /// CHECK-DAG: Mul loop:<<Loop>>
+ //
+ /// CHECK-START: int Main.geoMulBlackHole(int) loop_optimization (after)
+ /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0
+ /// CHECK-DAG: Return [<<Zero>>]
+ //
+ /// CHECK-START: int Main.geoMulBlackHole(int) loop_optimization (after)
+ /// CHECK-NOT: Phi
+ /// CHECK-NOT: Mul
+ public static int geoMulBlackHole(int a) {
+ for (int i = 0; i < 100; i++) {
+ a *= 10;
+ }
+ return a;
+ }
+
+ /// CHECK-START: int Main.geoShlBlackHole(int) loop_optimization (before)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>>
+ /// CHECK-DAG: Shl loop:<<Loop>>
+ //
+ /// CHECK-START: int Main.geoShlBlackHole(int) loop_optimization (after)
+ /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0
+ /// CHECK-DAG: Return [<<Zero>>]
+ //
+ /// CHECK-START: int Main.geoShlBlackHole(int) loop_optimization (after)
+ /// CHECK-NOT: Phi
+ /// CHECK-NOT: Shl
+ public static int geoShlBlackHole(int a) {
+ for (int i = 0; i < 100; i++) {
+ a <<= 1;
+ }
+ return a;
+ }
+
+ /// CHECK-START: int Main.geoDivBlackHole(int) loop_optimization (before)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>>
+ /// CHECK-DAG: Div loop:<<Loop>>
+ //
+ /// CHECK-START: int Main.geoDivBlackHole(int) loop_optimization (after)
+ /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0
+ /// CHECK-DAG: Return [<<Zero>>]
+ //
+ /// CHECK-START: int Main.geoDivBlackHole(int) loop_optimization (after)
+ /// CHECK-NOT: Phi
+ /// CHECK-NOT: Div
+ public static int geoDivBlackHole(int a) {
+ for (int i = 0; i < 100; i++) {
+ a /= 10;
+ }
+ return a;
+ }
+
+ // TODO: Rem is already optimized away by the time the loop optimizer runs;
+ // we could still optimize this case with last value on wrap-around!
+ //
+ /// CHECK-START: int Main.geoRemBlackHole(int) loop_optimization (before)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>>
+ /// CHECK-DAG: Phi loop:<<Loop>>
+ //
+ /// CHECK-START: int Main.geoRemBlackHole(int) loop_optimization (after)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>>
+ /// CHECK-DAG: Phi loop:<<Loop>>
+ public static int geoRemBlackHole(int a) {
+ for (int i = 0; i < 100; i++) {
+ a %= 1;
+ }
+ return a;
+ }
+
+ //
+ // Verifier.
+ //
+
+ public static void main(String[] args) {
+ int m = 1410065408;
+ for (int i = -100; i <= 100; i++) {
+ expectEquals(m * i, geo1(i));
+ }
+ for (int i = 1; i <= 1000000000; i *= 10) {
+ expectEquals( m * i, geo1( i));
+ expectEquals(-m * i, geo1(-i));
+ }
+
+ for (int i = -100; i <= 100; i++) {
+ expectEquals(i << 10, geo2(i));
+ }
+ for (int i = 0; i < 22; i++) {
+ expectEquals(1 << (i + 10), geo2(1 << i));
+ }
+ expectEquals(0x80000400, geo2(0x00200001));
+ expectEquals(0x00000000, geo2(0x00400000));
+ expectEquals(0x00000400, geo2(0x00400001));
+
+ int d = 59049;
+ for (int i = -100; i <= 100; i++) {
+ expectEquals(0, geo3(i));
+ }
+ for (int i = 1; i <= 100; i++) {
+ expectEquals( i, geo3( i * d));
+ expectEquals( i, geo3( i * d + 1));
+ expectEquals(-i, geo3(-i * d));
+ expectEquals(-i, geo3(-i * d - 1));
+ }
+
+ for (int i = -100; i <= 100; i++) {
+ expectEquals(i % 7, geo4(i));
+ }
+
+ expectEquals(34, geo1BCE());
+ expectEquals(36, geo2BCE());
+ expectEquals(131, geo3BCE());
+ expectEquals(125, geo4BCE());
+
+ // Nothing escapes!
+ expectEquals(0, geoMulBlackHole(0));
+ expectEquals(0, geoShlBlackHole(0));
+ expectEquals(0, geoDivBlackHole(0));
+ expectEquals(0, geoRemBlackHole(0));
+ for (int i = -100; i <= 100; i++) {
+ expectEquals(0, geoMulBlackHole(i));
+ expectEquals(0, geoShlBlackHole(i));
+ expectEquals(0, geoDivBlackHole(i));
+ expectEquals(0, geoRemBlackHole(i));
+ }
+ for (int i = 0; i < 31; i++) {
+ expectEquals(0, geoMulBlackHole(1 << i));
+ expectEquals(0, geoShlBlackHole(1 << i));
+ expectEquals(0, geoDivBlackHole(1 << i));
+ expectEquals(0, geoRemBlackHole(1 << i));
+ }
+ expectEquals(0, geoMulBlackHole(0x7fffffff));
+ expectEquals(0, geoShlBlackHole(0x7fffffff));
+ expectEquals(0, geoDivBlackHole(0x7fffffff));
+ expectEquals(0, geoRemBlackHole(0x7fffffff));
+ expectEquals(0, geoMulBlackHole(0x80000000));
+ expectEquals(0, geoShlBlackHole(0x80000000));
+ expectEquals(0, geoDivBlackHole(0x80000000));
+ expectEquals(0, geoRemBlackHole(0x80000000));
+
+ System.out.println("passed");
+ }
+
+ private static void expectEquals(int expected, int result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+}