}
/**
- * Returns true if instruction is proper entry-phi-operation for given loop
- * (referred to as mu-operation in Gerlek's paper).
- */
-static bool IsEntryPhi(HLoopInformation* loop, HInstruction* instruction) {
- return
- instruction->IsPhi() &&
- instruction->InputCount() == 2 &&
- instruction->GetBlock() == loop->GetHeader();
-}
-
-/**
* Since graph traversal may enter a SCC at any position, an initial representation may be rotated,
* along dependences, viz. any of (a, b, c, d), (d, a, b, c) (c, d, a, b), (b, c, d, a) assuming
* a chain of dependences (mutual independent items may occur in arbitrary order). For proper
size_t phi_pos = -1;
const size_t size = scc->size();
for (size_t i = 0; i < size; i++) {
- if (IsEntryPhi(loop, scc->at(i)) && (phi == nullptr || phis.FoundBefore(scc->at(i), phi))) {
- phi = scc->at(i);
+ HInstruction* other = scc->at(i);
+ if (other->IsLoopHeaderPhi() && (phi == nullptr || phis.FoundBefore(other, phi))) {
+ phi = other;
phi_pos = i;
}
}
}
// Classify the SCC.
- if (scc_.size() == 1 && !IsEntryPhi(loop, scc_[0])) {
+ if (scc_.size() == 1 && !scc_[0]->IsLoopHeaderPhi()) {
ClassifyTrivial(loop, scc_[0]);
} else {
ClassifyNonTrivial(loop);
void HInductionVarAnalysis::ClassifyTrivial(HLoopInformation* loop, HInstruction* instruction) {
InductionInfo* info = nullptr;
if (instruction->IsPhi()) {
- for (size_t i = 1, count = instruction->InputCount(); i < count; i++) {
- info = TransferPhi(LookupInfo(loop, instruction->InputAt(0)),
- LookupInfo(loop, instruction->InputAt(i)));
- }
+ info = TransferPhi(loop, instruction, /* input_index */ 0);
} else if (instruction->IsAdd()) {
info = TransferAddSub(LookupInfo(loop, instruction->InputAt(0)),
LookupInfo(loop, instruction->InputAt(1)), kAdd);
RotateEntryPhiFirst(loop, &scc_, &other);
}
- // Analyze from phi onwards.
+ // Analyze from entry-phi onwards.
HInstruction* phi = scc_[0];
- if (!IsEntryPhi(loop, phi)) {
+ if (!phi->IsLoopHeaderPhi()) {
return;
}
- HInstruction* external = phi->InputAt(0);
- HInstruction* internal = phi->InputAt(1);
- InductionInfo* initial = LookupInfo(loop, external);
+
+ // External link should be loop invariant.
+ InductionInfo* initial = LookupInfo(loop, phi->InputAt(0));
if (initial == nullptr || initial->induction_class != kInvariant) {
return;
}
- // Singleton entry-phi-operation may be a wrap-around induction.
+ // Singleton is wrap-around induction if all internal links have the same meaning.
if (size == 1) {
- InductionInfo* update = LookupInfo(loop, internal);
+ InductionInfo* update = TransferPhi(loop, phi, /* input_index */ 1);
if (update != nullptr) {
AssignInfo(loop, phi, CreateInduction(kWrapAround, initial, update));
}
HInstruction* instruction = scc_[i];
InductionInfo* update = nullptr;
if (instruction->IsPhi()) {
- update = SolvePhi(loop, phi, instruction);
+ update = SolvePhiAllInputs(loop, phi, instruction);
} else if (instruction->IsAdd()) {
update = SolveAddSub(
loop, phi, instruction, instruction->InputAt(0), instruction->InputAt(1), kAdd, true);
cycle_.Put(instruction, update);
}
- // Success if the internal link received a meaning.
- auto it = cycle_.find(internal);
- if (it != cycle_.end()) {
- InductionInfo* induction = it->second;
+ // Success if all internal links received the same temporary meaning.
+ InductionInfo* induction = SolvePhi(phi, /* input_index */ 1);
+ if (induction != nullptr) {
switch (induction->induction_class) {
case kInvariant:
// Classify first phi and then the rest of the cycle "on-demand".
return CreateInduction(kPeriodic, induction->op_a, RotatePeriodicInduction(induction->op_b, last));
}
-HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferPhi(InductionInfo* a,
- InductionInfo* b) {
- // Transfer over a phi: if both inputs are identical, result is input.
- if (InductionEqual(a, b)) {
- return a;
- }
- return nullptr;
+HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferPhi(HLoopInformation* loop,
+ HInstruction* phi,
+ size_t input_index) {
+ // Match all phi inputs from input_index onwards exactly.
+ const size_t count = phi->InputCount();
+ DCHECK_LT(input_index, count);
+ InductionInfo* a = LookupInfo(loop, phi->InputAt(input_index));
+ for (size_t i = input_index + 1; i < count; i++) {
+ InductionInfo* b = LookupInfo(loop, phi->InputAt(i));
+ if (!InductionEqual(a, b)) {
+ return nullptr;
+ }
+ }
+ return a;
}
HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferAddSub(InductionInfo* a,
return nullptr;
}
-HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::SolvePhi(HLoopInformation* loop,
- HInstruction* phi,
- HInstruction* instruction) {
- // Solve within a cycle over a phi: identical inputs are combined into that input as result.
- const size_t count = instruction->InputCount();
- DCHECK_GT(count, 0u);
- auto ita = cycle_.find(instruction->InputAt(0));
+HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::SolvePhi(HInstruction* phi,
+ size_t input_index) {
+ // Match all phi inputs from input_index onwards exactly.
+ const size_t count = phi->InputCount();
+ DCHECK_LT(input_index, count);
+ auto ita = cycle_.find(phi->InputAt(input_index));
if (ita != cycle_.end()) {
- InductionInfo* a = ita->second;
- for (size_t i = 1; i < count; i++) {
- auto itb = cycle_.find(instruction->InputAt(i));
- if (itb == cycle_.end() || !HInductionVarAnalysis::InductionEqual(a, itb->second)) {
+ for (size_t i = input_index + 1; i < count; i++) {
+ auto itb = cycle_.find(phi->InputAt(i));
+ if (itb == cycle_.end() ||
+ !HInductionVarAnalysis::InductionEqual(ita->second, itb->second)) {
return nullptr;
}
}
- return a;
+ return ita->second;
}
+ return nullptr;
+}
- // Solve within a cycle over another entry-phi: add invariants into a periodic.
- if (IsEntryPhi(loop, instruction)) {
- InductionInfo* a = LookupInfo(loop, instruction->InputAt(0));
+HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::SolvePhiAllInputs(
+ HLoopInformation* loop,
+ HInstruction* entry_phi,
+ HInstruction* phi) {
+ // Match all phi inputs.
+ InductionInfo* match = SolvePhi(phi, /* input_index */ 0);
+ if (match != nullptr) {
+ return match;
+ }
+
+ // Otherwise, try to solve for a periodic seeded from phi onward.
+ // Only tight multi-statement cycles are considered in order to
+ // simplify rotating the periodic during the final classification.
+ if (phi->IsLoopHeaderPhi() && phi->InputCount() == 2) {
+ InductionInfo* a = LookupInfo(loop, phi->InputAt(0));
if (a != nullptr && a->induction_class == kInvariant) {
- if (instruction->InputAt(1) == phi) {
- InductionInfo* initial = LookupInfo(loop, phi->InputAt(0));
+ if (phi->InputAt(1) == entry_phi) {
+ InductionInfo* initial = LookupInfo(loop, entry_phi->InputAt(0));
return CreateInduction(kPeriodic, a, initial);
}
- auto it = cycle_.find(instruction->InputAt(1));
- if (it != cycle_.end()) {
- InductionInfo* b = it->second;
- if (b->induction_class == kPeriodic) {
- return CreateInduction(kPeriodic, a, b);
- }
+ InductionInfo* b = SolvePhi(phi, /* input_index */ 1);
+ if (b != nullptr && b->induction_class == kPeriodic) {
+ return CreateInduction(kPeriodic, a, b);
}
}
}
-
return nullptr;
}
HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::SolveAddSub(HLoopInformation* loop,
- HInstruction* phi,
+ HInstruction* entry_phi,
HInstruction* instruction,
HInstruction* x,
HInstruction* y,
// invariant value, seeded from phi, keeps adding to the stride of the induction.
InductionInfo* b = LookupInfo(loop, y);
if (b != nullptr && b->induction_class == kInvariant) {
- if (x == phi) {
+ if (x == entry_phi) {
return (op == kAdd) ? b : CreateInvariantOp(kNeg, nullptr, b);
}
auto it = cycle_.find(x);
if (op == kAdd) {
// Try the other way around for an addition if considered for first time.
if (is_first_call) {
- return SolveAddSub(loop, phi, instruction, y, x, op, false);
+ return SolveAddSub(loop, entry_phi, instruction, y, x, op, false);
}
} else if (op == kSub) {
- // Solve within a tight cycle for a periodic idiom k = c - k;
- if (y == phi && instruction == phi->InputAt(1)) {
+ // 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;
+ 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, phi->InputAt(0));
+ InductionInfo* initial = LookupInfo(loop, entry_phi->InputAt(0));
return CreateInduction(kPeriodic, CreateInvariantOp(kSub, a, initial), initial);
}
}
Primitive::Type type,
IfCondition cmp) {
if (a->induction_class == kInvariant && b->induction_class == kLinear) {
- // Swap conditions (e.g. U > i is same as i < U).
+ // Swap condition if induction is at right-hand-side (e.g. U > i is same as i < U).
switch (cmp) {
case kCondLT: VisitCondition(loop, b, a, type, kCondGT); break;
case kCondLE: VisitCondition(loop, b, a, type, kCondGE); break;
case kCondGT: VisitCondition(loop, b, a, type, kCondLT); break;
case kCondGE: VisitCondition(loop, b, a, type, kCondLE); break;
+ case kCondNE: VisitCondition(loop, b, a, type, kCondNE); break;
default: break;
}
} else if (a->induction_class == kLinear && b->induction_class == kInvariant) {
- // Normalize a linear loop control with a constant, nonzero stride:
- // stride > 0, either i < U or i <= U
- // stride < 0, either i > U or i >= U
+ // Analyze condition with induction at left-hand-side (e.g. i < U).
InductionInfo* stride = a->op_a;
InductionInfo* lo_val = a->op_b;
InductionInfo* hi_val = b;
- // Analyze the stride thoroughly, since its representation may be compound at this point.
+ // Analyze stride (may be compound).
InductionVarRange::Value v1 = InductionVarRange::GetMin(stride, nullptr);
InductionVarRange::Value v2 = InductionVarRange::GetMax(stride, nullptr);
- if (v1.a_constant == 0 && v2.a_constant == 0 && v1.b_constant == v2.b_constant) {
- const int32_t stride_value = v1.b_constant;
- if ((stride_value > 0 && (cmp == kCondLT || cmp == kCondLE)) ||
- (stride_value < 0 && (cmp == kCondGT || cmp == kCondGE))) {
- bool is_strict = cmp == kCondLT || cmp == kCondGT;
- VisitTripCount(loop, lo_val, hi_val, stride, stride_value, type, is_strict);
+ if (v1.a_constant != 0 || v2.a_constant != 0 || v1.b_constant != v2.b_constant) {
+ return;
+ }
+ // Rewrite safe condition i != U with unit stride into i < U or i > U
+ // (unit stride guarantees that the end condition is always reached).
+ const int32_t stride_value = v1.b_constant;
+ int64_t lo_value = 0;
+ int64_t hi_value = 0;
+ if (cmp == kCondNE && IsIntAndGet(lo_val, &lo_value) && IsIntAndGet(hi_val, &hi_value)) {
+ if ((stride_value == +1 && lo_value < hi_value) ||
+ (stride_value == -1 && lo_value > hi_value)) {
+ cmp = stride_value > 0 ? kCondLT : kCondGT;
}
}
+ // Normalize a linear loop control with a nonzero stride:
+ // stride > 0, either i < U or i <= U
+ // stride < 0, either i > U or i >= U
+ //
+ // TODO: construct conditions for constant/symbolic safety of trip-count
+ //
+ if ((stride_value > 0 && (cmp == kCondLT || cmp == kCondLE)) ||
+ (stride_value < 0 && (cmp == kCondGT || cmp == kCondGE))) {
+ VisitTripCount(loop, lo_val, hi_val, stride, stride_value, type, cmp);
+ }
}
}
InductionInfo* stride,
int32_t stride_value,
Primitive::Type type,
- bool is_strict) {
+ IfCondition cmp) {
// Any loop of the general form:
//
// for (i = L; i <= U; i += S) // S > 0
// for (n = 0; n < TC; n++) // where TC = (U + S - L) / S
// .. L + S * n ..
//
- // NOTE: The TC (trip-count) expression is only valid if the top-test path is taken at
- // least once. Otherwise TC is 0. Also, the expression assumes the loop does not
- // have any early-exits. Otherwise, TC is an upper bound.
+ // NOTE: The TC (trip-count) expression is only valid when safe. Otherwise TC is 0
+ // (or possibly infinite). Also, the expression assumes the loop does not have
+ // early-exits. Otherwise, TC is an upper bound.
//
- bool cancels = is_strict && std::abs(stride_value) == 1; // compensation cancels conversion?
+ bool cancels = (cmp == kCondLT || cmp == kCondGT) && std::abs(stride_value) == 1;
if (!cancels) {
// Convert exclusive integral inequality into inclusive integral inequality,
// viz. condition i < U is i <= U - 1 and condition i > U is i >= U + 1.
- if (is_strict) {
- const InductionOp op = stride_value > 0 ? kSub : kAdd;
- hi_val = CreateInvariantOp(op, hi_val, CreateConstant(1, type));
+ if (cmp == kCondLT) {
+ hi_val = CreateInvariantOp(kSub, hi_val, CreateConstant(1, type));
+ } else if (cmp == kCondGT) {
+ hi_val = CreateInvariantOp(kAdd, hi_val, CreateConstant(1, type));
}
// Compensate for stride.
hi_val = CreateInvariantOp(kAdd, hi_val, stride);
}
// Assign the trip-count expression to the loop control. Clients that use the information
- // should be aware that due to the top-test assumption, the expression is only valid in the
- // loop-body proper, and not yet in the loop-header. If the loop has any early exits, the
- // trip-count forms a conservative upper bound on the number of loop iterations.
+ // should be aware that the expression is only valid in the loop-body proper (when symbolically
+ // safe), and not yet in the loop-header (unless constant safe). If the loop has any early exits,
+ // the trip-count forms a conservative upper bound on the number of loop iterations.
InductionInfo* trip_count =
CreateInvariantOp(kDiv, CreateInvariantOp(kSub, hi_val, lo_val), stride);
AssignInfo(loop, loop->GetHeader()->GetLastInstruction(), trip_count);
uint32_t VisitDescendant(HLoopInformation* loop, HInstruction* instruction);
void ClassifyTrivial(HLoopInformation* loop, HInstruction* instruction);
void ClassifyNonTrivial(HLoopInformation* loop);
+ InductionInfo* RotatePeriodicInduction(InductionInfo* induction, InductionInfo* last);
// Transfer operations.
- InductionInfo* TransferPhi(InductionInfo* a, InductionInfo* b);
+ 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);
// Solvers.
- InductionInfo* SolvePhi(HLoopInformation* loop,
- HInstruction* phi,
- HInstruction* instruction);
+ InductionInfo* SolvePhi(HInstruction* phi, size_t input_index);
+ InductionInfo* SolvePhiAllInputs(HLoopInformation* loop,
+ HInstruction* entry_phi,
+ HInstruction* phi);
InductionInfo* SolveAddSub(HLoopInformation* loop,
- HInstruction* phi,
+ HInstruction* entry_phi,
HInstruction* instruction,
HInstruction* x,
HInstruction* y,
InductionOp op,
bool is_first_call);
- InductionInfo* RotatePeriodicInduction(InductionInfo* induction, InductionInfo* last);
// Trip count information.
void VisitControl(HLoopInformation* loop);
InductionInfo* stride,
int32_t stride_value,
Primitive::Type type,
- bool is_strict);
+ IfCondition cmp);
// Assign and lookup.
void AssignInfo(HLoopInformation* loop, HInstruction* instruction, InductionInfo* info);
#include "builder.h"
#include "gtest/gtest.h"
#include "induction_var_analysis.h"
+#include "induction_var_range.h"
#include "nodes.h"
#include "optimizing_unit_test.h"
HInstruction* store = InsertArrayStore(induc_, 0);
InsertLocalStore(induc_, InsertLocalLoad(tmp_, 0), 0);
HInstruction *sub = InsertInstruction(
- new (&allocator_) HSub(Primitive::kPrimInt, constant100_, InsertLocalLoad(basic_[0], 0)), 0);
+ new (&allocator_) HSub(Primitive::kPrimInt, constant100_, InsertLocalLoad(basic_[0], 0)), 0);
InsertLocalStore(tmp_, sub, 0);
PerformInductionVarAnalysis();
new (&allocator_) HAdd(Primitive::kPrimInt, InsertLocalLoad(induc_, 0), constant100_), 0);
InsertLocalStore(tmp_, add, 0);
HInstruction *sub = InsertInstruction(
- new (&allocator_) HSub(Primitive::kPrimInt, InsertLocalLoad(induc_, 0), constant100_), 0);
+ new (&allocator_) HSub(Primitive::kPrimInt, InsertLocalLoad(induc_, 0), constant100_), 0);
InsertLocalStore(tmp_, sub, 0);
HInstruction *mul = InsertInstruction(
- new (&allocator_) HMul(Primitive::kPrimInt, InsertLocalLoad(induc_, 0), constant100_), 0);
+ new (&allocator_) HMul(Primitive::kPrimInt, InsertLocalLoad(induc_, 0), constant100_), 0);
InsertLocalStore(tmp_, mul, 0);
HInstruction *shl = InsertInstruction(
- new (&allocator_) HShl(Primitive::kPrimInt, InsertLocalLoad(induc_, 0), constant1_), 0);
+ new (&allocator_) HShl(Primitive::kPrimInt, InsertLocalLoad(induc_, 0), constant1_), 0);
InsertLocalStore(tmp_, shl, 0);
HInstruction *neg = InsertInstruction(
- new (&allocator_) HNeg(Primitive::kPrimInt, InsertLocalLoad(induc_, 0)), 0);
+ new (&allocator_) HNeg(Primitive::kPrimInt, InsertLocalLoad(induc_, 0)), 0);
InsertLocalStore(tmp_, neg, 0);
InsertLocalStore(
induc_,
BuildLoopNest(1);
HInstruction* store = InsertArrayStore(induc_, 0);
HInstruction *sub = InsertInstruction(
- new (&allocator_) HSub(Primitive::kPrimInt, constant1_, InsertLocalLoad(induc_, 0)), 0);
+ new (&allocator_) HSub(Primitive::kPrimInt, constant1_, InsertLocalLoad(induc_, 0)), 0);
InsertLocalStore(induc_, sub, 0);
PerformInductionVarAnalysis();
HSub(Primitive::kPrimInt, constant1_, InsertLocalLoad(induc_, 0)), 0), 0);
// Derived expressions.
HInstruction *add = InsertInstruction(
- new (&allocator_) HAdd(Primitive::kPrimInt, InsertLocalLoad(induc_, 0), constant100_), 0);
+ new (&allocator_) HAdd(Primitive::kPrimInt, InsertLocalLoad(induc_, 0), constant100_), 0);
InsertLocalStore(tmp_, add, 0);
HInstruction *sub = InsertInstruction(
- new (&allocator_) HSub(Primitive::kPrimInt, InsertLocalLoad(induc_, 0), constant100_), 0);
+ new (&allocator_) HSub(Primitive::kPrimInt, InsertLocalLoad(induc_, 0), constant100_), 0);
InsertLocalStore(tmp_, sub, 0);
HInstruction *mul = InsertInstruction(
- new (&allocator_) HMul(Primitive::kPrimInt, InsertLocalLoad(induc_, 0), constant100_), 0);
+ new (&allocator_) HMul(Primitive::kPrimInt, InsertLocalLoad(induc_, 0), constant100_), 0);
InsertLocalStore(tmp_, mul, 0);
HInstruction *shl = InsertInstruction(
- new (&allocator_) HShl(Primitive::kPrimInt, InsertLocalLoad(induc_, 0), constant1_), 0);
+ new (&allocator_) HShl(Primitive::kPrimInt, InsertLocalLoad(induc_, 0), constant1_), 0);
InsertLocalStore(tmp_, shl, 0);
HInstruction *neg = InsertInstruction(
- new (&allocator_) HNeg(Primitive::kPrimInt, InsertLocalLoad(induc_, 0)), 0);
+ new (&allocator_) HNeg(Primitive::kPrimInt, InsertLocalLoad(induc_, 0)), 0);
InsertLocalStore(tmp_, neg, 0);
PerformInductionVarAnalysis();
EXPECT_STREQ("periodic(( - (1)), (0))", GetInductionInfo(neg, 0).c_str());
}
+TEST_F(InductionVarAnalysisTest, FindRange) {
+ // Setup:
+ // for (int i = 0; i < 100; i++) {
+ // k = i << 1;
+ // k = k + 1;
+ // a[k] = 0;
+ // }
+ BuildLoopNest(1);
+ HInstruction *shl = InsertInstruction(
+ new (&allocator_) HShl(Primitive::kPrimInt, InsertLocalLoad(basic_[0], 0), constant1_), 0);
+ InsertLocalStore(induc_, shl, 0);
+ HInstruction *add = InsertInstruction(
+ new (&allocator_) HAdd(Primitive::kPrimInt, InsertLocalLoad(induc_, 0), constant1_), 0);
+ InsertLocalStore(induc_, add, 0);
+ HInstruction* store = InsertArrayStore(induc_, 0);
+ PerformInductionVarAnalysis();
+
+ EXPECT_STREQ("((2) * i + (1))", GetInductionInfo(store->InputAt(1), 0).c_str());
+
+ InductionVarRange range(iva_);
+ InductionVarRange::Value v_min = range.GetMinInduction(store, store->InputAt(1));
+ InductionVarRange::Value v_max = range.GetMaxInduction(store, store->InputAt(1));
+ EXPECT_EQ(0, v_min.a_constant);
+ EXPECT_EQ(1, v_min.b_constant);
+ EXPECT_EQ(0, v_max.a_constant);
+ EXPECT_EQ(199, v_max.b_constant);
+}
+
TEST_F(InductionVarAnalysisTest, FindDeepLoopInduction) {
// Setup:
// k = 0;
return result;
}
+ /// CHECK-START: int Main.linearVeryObscure(int[]) BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ /// CHECK-START: int Main.linearVeryObscure(int[]) BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ private static int linearVeryObscure(int[] x) {
+ int result = 0;
+ for (int i = 0; i < x.length; i++) {
+ int k = (-i) + (i << 5) + i - (32 * i) + 5 + (int) i;
+ result += x[k - 5];
+ }
+ return result;
+ }
+
/// CHECK-START: int Main.linearWhile(int[]) BCE (before)
/// CHECK-DAG: BoundsCheck
/// CHECK-START: int Main.linearWhile(int[]) BCE (after)
return result;
}
+ /// CHECK-START: int Main.linearThreeWayPhi(int[]) BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ /// CHECK-START: int Main.linearThreeWayPhi(int[]) BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ private static int linearThreeWayPhi(int[] x) {
+ int result = 0;
+ for (int i = 0; i < x.length; ) {
+ if (x[i] == 5) {
+ i++;
+ continue;
+ }
+ result += x[i++];
+ }
+ return result;
+ }
+
+ /// CHECK-START: int Main.linearFourWayPhi(int[]) BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ /// CHECK-START: int Main.linearFourWayPhi(int[]) BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ private static int linearFourWayPhi(int[] x) {
+ int result = 0;
+ for (int i = 0; i < x.length; ) {
+ if (x[i] == 5) {
+ i++;
+ continue;
+ } else if (x[i] == 6) {
+ i++;
+ result += 7;
+ continue;
+ }
+ result += x[i++];
+ }
+ return result;
+ }
+
/// CHECK-START: int Main.wrapAroundThenLinear(int[]) BCE (before)
/// CHECK-DAG: BoundsCheck
/// CHECK-START: int Main.wrapAroundThenLinear(int[]) BCE (after)
return result;
}
+ /// CHECK-START: int Main.wrapAroundThenLinearThreeWayPhi(int[]) BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ /// CHECK-START: int Main.wrapAroundThenLinearThreeWayPhi(int[]) BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ private static int wrapAroundThenLinearThreeWayPhi(int[] x) {
+ // Loop with wrap around (length - 1, 0, 1, 2, ..).
+ int w = x.length - 1;
+ int result = 0;
+ for (int i = 0; i < x.length; ) {
+ if (x[w] == 1) {
+ w = i++;
+ continue;
+ }
+ result += x[w];
+ w = i++;
+ }
+ return result;
+ }
+
/// CHECK-START: int[] Main.linearWithParameter(int) BCE (before)
/// CHECK-DAG: BoundsCheck
/// CHECK-START: int[] Main.linearWithParameter(int) BCE (after)
return x;
}
+ /// CHECK-START: int[] Main.linearCopy(int[]) BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ /// CHECK-START: int[] Main.linearCopy(int[]) BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ private static int[] linearCopy(int x[]) {
+ int n = x.length;
+ int y[] = new int[n];
+ for (int i = 0; i < n; i++) {
+ y[i] = x[i];
+ }
+ return y;
+ }
+
/// CHECK-START: int Main.linearWithCompoundStride() BCE (before)
/// CHECK-DAG: BoundsCheck
/// CHECK-START: int Main.linearWithCompoundStride() BCE (after)
int result = 0;
int k = 0;
// Range analysis has no problem with a trip-count defined by a
- // reasonably large positive stride.
+ // reasonably large positive stride far away from upper bound.
for (int i = 1; i <= 10 * 10000000 + 1; i += 10000000) {
result += x[k++];
}
int k = 0;
// Range analysis conservatively bails due to potential of wrap-around
// arithmetic while computing the trip-count for this very large stride.
- for (int i = 1; i < 2147483647; i += 195225786) {
+ for (int i = 1; i < Integer.MAX_VALUE; i += 195225786) {
result += x[k++];
}
return result;
int result = 0;
int k = 0;
// Range analysis has no problem with a trip-count defined by a
- // reasonably large negative stride.
+ // reasonably large negative stride far away from lower bound.
for (int i = -1; i >= -10 * 10000000 - 1; i -= 10000000) {
result += x[k++];
}
int k = 0;
// Range analysis conservatively bails due to potential of wrap-around
// arithmetic while computing the trip-count for this very large stride.
- for (int i = -2; i > -2147483648; i -= 195225786) {
+ for (int i = -2; i > Integer.MIN_VALUE; i -= 195225786) {
result += x[k++];
}
return result;
}
+ /// CHECK-START: int Main.linearForNE() BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ /// CHECK-START: int Main.linearForNE() BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ private static int linearForNE() {
+ int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+ int result = 0;
+ for (int i = 0; i != 10; i++) {
+ result += x[i];
+ }
+ return result;
+ }
+
+ /// CHECK-START: int Main.linearDoWhile() BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ /// CHECK-START: int Main.linearDoWhile() BCE (after)
+ /// CHECK-DAG: BoundsCheck
+ private static int linearDoWhile() {
+ int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+ int result = 0;
+ int i = 0;
+ // TODO: make this work
+ do {
+ result += x[i++];
+ } while (i < 10);
+ return result;
+ }
+
+ /// CHECK-START: int Main.linearShort() BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ /// CHECK-START: int Main.linearShort() BCE (after)
+ /// CHECK-DAG: BoundsCheck
+ private static int linearShort() {
+ int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+ int result = 0;
+ // TODO: make this work
+ for (short i = 0; i < 10; i++) {
+ result += x[i];
+ }
+ return result;
+ }
+
/// CHECK-START: int Main.periodicIdiom(int) BCE (before)
/// CHECK-DAG: BoundsCheck
/// CHECK-START: int Main.periodicIdiom(int) BCE (after)
return result;
}
+ /// CHECK-START: int Main.justRightUp1() BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ /// CHECK-START: int Main.justRightUp1() BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ private static int justRightUp1() {
+ int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+ int result = 0;
+ for (int i = Integer.MAX_VALUE - 10, k = 0; i < Integer.MAX_VALUE; i++) {
+ result += x[k++];
+ }
+ return result;
+ }
+
+ /// CHECK-START: int Main.justRightUp2() BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ /// CHECK-START: int Main.justRightUp2() BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ private static int justRightUp2() {
+ int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+ int result = 0;
+ for (int i = Integer.MAX_VALUE - 10; i < Integer.MAX_VALUE; i++) {
+ result += x[i - Integer.MAX_VALUE + 10];
+ }
+ return result;
+ }
+
+ /// CHECK-START: int Main.justRightUp3() BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ /// CHECK-START: int Main.justRightUp3() BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ private static int justRightUp3() {
+ int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+ int result = 0;
+ for (int i = Integer.MAX_VALUE - 10, k = 0; i <= Integer.MAX_VALUE - 1; i++) {
+ result += x[k++];
+ }
+ return result;
+ }
+
+ /// CHECK-START: int Main.justOOBUp() BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ /// CHECK-START: int Main.justOOBUp() BCE (after)
+ /// CHECK-DAG: BoundsCheck
+ private static int justOOBUp() {
+ int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+ int result = 0;
+ // Infinite loop!
+ for (int i = Integer.MAX_VALUE - 9, k = 0; i <= Integer.MAX_VALUE; i++) {
+ result += x[k++];
+ }
+ return result;
+ }
+
+ /// CHECK-START: int Main.justRightDown1() BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ /// CHECK-START: int Main.justRightDown1() BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ private static int justRightDown1() {
+ int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+ int result = 0;
+ for (int i = Integer.MIN_VALUE + 10, k = 0; i > Integer.MIN_VALUE; i--) {
+ result += x[k++];
+ }
+ return result;
+ }
+
+ /// CHECK-START: int Main.justRightDown2() BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ /// CHECK-START: int Main.justRightDown2() BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ private static int justRightDown2() {
+ int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+ int result = 0;
+ for (int i = Integer.MIN_VALUE + 10; i > Integer.MIN_VALUE; i--) {
+ result += x[Integer.MAX_VALUE + i];
+ }
+ return result;
+ }
+
+ /// CHECK-START: int Main.justRightDown3() BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ /// CHECK-START: int Main.justRightDown3() BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ private static int justRightDown3() {
+ int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+ int result = 0;
+ for (int i = Integer.MIN_VALUE + 10, k = 0; i >= Integer.MIN_VALUE + 1; i--) {
+ result += x[k++];
+ }
+ return result;
+ }
+
+ /// CHECK-START: int Main.justOOBDown() BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ /// CHECK-START: int Main.justOOBDown() BCE (after)
+ /// CHECK-DAG: BoundsCheck
+ private static int justOOBDown() {
+ int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+ int result = 0;
+ // Infinite loop!
+ for (int i = Integer.MIN_VALUE + 9, k = 0; i >= Integer.MIN_VALUE; i--) {
+ result += x[k++];
+ }
+ return result;
+ }
+
//
// Cases that actually go out of bounds. These test cases
// ensure the exceptions are thrown at the right places.
expectEquals(55, linearDown(x));
expectEquals(0, linearObscure(empty));
expectEquals(55, linearObscure(x));
+ expectEquals(0, linearVeryObscure(empty));
+ expectEquals(55, linearVeryObscure(x));
expectEquals(0, linearWhile(empty));
expectEquals(55, linearWhile(x));
+ expectEquals(0, linearThreeWayPhi(empty));
+ expectEquals(50, linearThreeWayPhi(x));
+ expectEquals(0, linearFourWayPhi(empty));
+ expectEquals(51, linearFourWayPhi(x));
expectEquals(0, wrapAroundThenLinear(empty));
expectEquals(55, wrapAroundThenLinear(x));
+ expectEquals(0, wrapAroundThenLinearThreeWayPhi(empty));
+ expectEquals(54, wrapAroundThenLinearThreeWayPhi(x));
// Linear with parameter.
sResult = 0;
}
}
+ // Linear copy.
+ expectEquals(0, linearCopy(empty).length);
+ {
+ int[] r = linearCopy(x);
+ expectEquals(x.length, r.length);
+ for (int i = 0; i < x.length; i++) {
+ expectEquals(x[i], r[i]);
+ }
+ }
+
// Linear with non-unit strides.
expectEquals(56, linearWithCompoundStride());
expectEquals(66, linearWithLargePositiveStride());
expectEquals(66, linearWithLargeNegativeStride());
expectEquals(66, linearWithVeryLargeNegativeStride());
+ // Special forms.
+ expectEquals(55, linearForNE());
+ expectEquals(55, linearDoWhile());
+ expectEquals(55, linearShort());
+
// Periodic adds (1, 3), one at the time.
expectEquals(0, periodicIdiom(-1));
for (int tc = 0; tc < 32; tc++) {
expectEquals(tc * 16, periodicSequence4(tc));
}
+ // Large bounds.
+ expectEquals(55, justRightUp1());
+ expectEquals(55, justRightUp2());
+ expectEquals(55, justRightUp3());
+ expectEquals(55, justRightDown1());
+ expectEquals(55, justRightDown2());
+ expectEquals(55, justRightDown3());
+ sResult = 0;
+ try {
+ justOOBUp();
+ } catch (ArrayIndexOutOfBoundsException e) {
+ sResult = 1;
+ }
+ expectEquals(1, sResult);
+ sResult = 0;
+ try {
+ justOOBDown();
+ } catch (ArrayIndexOutOfBoundsException e) {
+ sResult = 1;
+ }
+ expectEquals(1, sResult);
+
// Lower bound goes OOB.
sResult = 0;
try {