Merge "Remove Quick from tree."
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index 0b69810..07bd0e3 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -229,6 +229,7 @@
CHECK_EQ(image_header->storage_mode_, image_storage_mode_);
switch (image_storage_mode_) {
+ case ImageHeader::kStorageModeLZ4HC: // Fall-through.
case ImageHeader::kStorageModeLZ4: {
const size_t compressed_max_size = LZ4_compressBound(image_data_size);
compressed_data.reset(new char[compressed_max_size]);
@@ -239,6 +240,8 @@
break;
}
+ /*
+ * Disabled due to image_test64 flakyness. Both use same decompression. b/27560444
case ImageHeader::kStorageModeLZ4HC: {
// Bound is same as non HC.
const size_t compressed_max_size = LZ4_compressBound(image_data_size);
@@ -249,6 +252,7 @@
image_data_size);
break;
}
+ */
case ImageHeader::kStorageModeUncompressed: {
data_size = image_data_size;
image_data_to_write = image_data;
@@ -264,6 +268,16 @@
image_data_to_write = &compressed_data[0];
VLOG(compiler) << "Compressed from " << image_data_size << " to " << data_size << " in "
<< PrettyDuration(NanoTime() - compress_start_time);
+ if (kIsDebugBuild) {
+ std::unique_ptr<uint8_t[]> temp(new uint8_t[image_data_size]);
+ const size_t decompressed_size = LZ4_decompress_safe(
+ reinterpret_cast<char*>(&compressed_data[0]),
+ reinterpret_cast<char*>(&temp[0]),
+ data_size,
+ image_data_size);
+ CHECK_EQ(decompressed_size, image_data_size);
+ CHECK_EQ(memcmp(image_data, &temp[0], image_data_size), 0) << image_storage_mode_;
+ }
}
// Write out the image + fields + methods.
@@ -2024,7 +2038,6 @@
memcpy(copy, orig, ArtMethod::Size(target_ptr_size_));
copy->SetDeclaringClass(GetImageAddress(orig->GetDeclaringClassUnchecked()));
-
ArtMethod** orig_resolved_methods = orig->GetDexCacheResolvedMethods(target_ptr_size_);
copy->SetDexCacheResolvedMethods(NativeLocationInImage(orig_resolved_methods), target_ptr_size_);
GcRoot<mirror::Class>* orig_resolved_types = orig->GetDexCacheResolvedTypes(target_ptr_size_);
diff --git a/compiler/optimizing/induction_var_analysis.cc b/compiler/optimizing/induction_var_analysis.cc
index 82a898a..8d24e26 100644
--- a/compiler/optimizing/induction_var_analysis.cc
+++ b/compiler/optimizing/induction_var_analysis.cc
@@ -53,6 +53,32 @@
}
}
+/**
+ * Returns true if the from/to types denote a narrowing, integral conversion (precision loss).
+ */
+static bool IsNarrowingIntegralConversion(Primitive::Type from, Primitive::Type to) {
+ switch (from) {
+ case Primitive::kPrimLong:
+ return to == Primitive::kPrimByte || to == Primitive::kPrimShort
+ || to == Primitive::kPrimChar || to == Primitive::kPrimInt;
+ case Primitive::kPrimInt:
+ return to == Primitive::kPrimByte || to == Primitive::kPrimShort
+ || to == Primitive::kPrimChar;
+ case Primitive::kPrimChar:
+ case Primitive::kPrimShort:
+ return to == Primitive::kPrimByte;
+ default:
+ return false;
+ }
+}
+
+/**
+ * Returns narrowest data type.
+ */
+static Primitive::Type Narrowest(Primitive::Type type1, Primitive::Type type2) {
+ return Primitive::ComponentSize(type1) <= Primitive::ComponentSize(type2) ? type1 : type2;
+}
+
//
// Class methods.
//
@@ -148,6 +174,9 @@
}
}
+ // Type of induction.
+ type_ = scc_[0]->GetType();
+
// Classify the SCC.
if (scc_.size() == 1 && !scc_[0]->IsLoopHeaderPhi()) {
ClassifyTrivial(loop, scc_[0]);
@@ -197,14 +226,13 @@
instruction->InputAt(0)->GetType());
} else if (instruction->IsNeg()) {
info = TransferNeg(LookupInfo(loop, instruction->InputAt(0)));
+ } 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.
- } else if (instruction->IsTypeConversion()) {
- HTypeConversion* conversion = instruction->AsTypeConversion();
- // TODO: accept different conversion scenarios.
- if (conversion->GetResultType() == conversion->GetInputType()) {
- info = LookupInfo(loop, conversion->GetInput());
- }
}
// Successfully classified?
@@ -239,7 +267,7 @@
if (size == 1) {
InductionInfo* update = TransferPhi(loop, phi, /* input_index */ 1);
if (update != nullptr) {
- AssignInfo(loop, phi, CreateInduction(kWrapAround, initial, update));
+ AssignInfo(loop, phi, CreateInduction(kWrapAround, initial, update, type_));
}
return;
}
@@ -257,6 +285,8 @@
} else if (instruction->IsSub()) {
update = SolveAddSub(
loop, phi, instruction, instruction->InputAt(0), instruction->InputAt(1), kSub, true);
+ } else if (instruction->IsTypeConversion()) {
+ update = SolveCnv(instruction->AsTypeConversion());
}
if (update == nullptr) {
return;
@@ -271,7 +301,7 @@
case kInvariant:
// Classify first phi and then the rest of the cycle "on-demand".
// Statements are scanned in order.
- AssignInfo(loop, phi, CreateInduction(kLinear, induction, initial));
+ AssignInfo(loop, phi, CreateInduction(kLinear, induction, initial, type_));
for (size_t i = 1; i < size; i++) {
ClassifyTrivial(loop, scc_[i]);
}
@@ -301,9 +331,10 @@
// (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);
+ return CreateInduction(kPeriodic, induction, last, type_);
}
- return CreateInduction(kPeriodic, induction->op_a, RotatePeriodicInduction(induction->op_b, last));
+ return CreateInduction(
+ kPeriodic, induction->op_a, RotatePeriodicInduction(induction->op_b, last), type_);
}
HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferPhi(HLoopInformation* loop,
@@ -332,8 +363,10 @@
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, TransferAddSub(a->op_a, b->op_a, op), TransferAddSub(a->op_b, b->op_b, op));
+ return CreateInduction(kLinear,
+ TransferAddSub(a->op_a, b->op_a, op),
+ TransferAddSub(a->op_b, b->op_b, op),
+ type_);
} else if (a->induction_class == kInvariant) {
InductionInfo* new_a = b->op_a;
InductionInfo* new_b = TransferAddSub(a, b->op_b, op);
@@ -343,7 +376,7 @@
} else if (op == kSub) { // Negation required.
new_a = TransferNeg(new_a);
}
- return CreateInduction(b->induction_class, new_a, new_b);
+ return CreateInduction(b->induction_class, new_a, new_b, type_);
} else if (b->induction_class == kInvariant) {
InductionInfo* new_a = a->op_a;
InductionInfo* new_b = TransferAddSub(a->op_b, b, op);
@@ -351,7 +384,7 @@
DCHECK(a->induction_class == kWrapAround || a->induction_class == kPeriodic);
new_a = TransferAddSub(new_a, b, op);
}
- return CreateInduction(a->induction_class, new_a, new_b);
+ return CreateInduction(a->induction_class, new_a, new_b, type_);
}
}
return nullptr;
@@ -366,9 +399,15 @@
if (a->induction_class == kInvariant && b->induction_class == kInvariant) {
return CreateInvariantOp(kMul, a, b);
} else if (a->induction_class == kInvariant) {
- return CreateInduction(b->induction_class, TransferMul(a, b->op_a), TransferMul(a, b->op_b));
+ return CreateInduction(b->induction_class,
+ TransferMul(a, b->op_a),
+ TransferMul(a, b->op_b),
+ type_);
} else if (b->induction_class == kInvariant) {
- return CreateInduction(a->induction_class, TransferMul(a->op_a, b), TransferMul(a->op_b, b));
+ return CreateInduction(a->induction_class,
+ TransferMul(a->op_a, b),
+ TransferMul(a->op_b, b),
+ type_);
}
}
return nullptr;
@@ -400,7 +439,24 @@
if (a->induction_class == kInvariant) {
return CreateInvariantOp(kNeg, nullptr, a);
}
- return CreateInduction(a->induction_class, TransferNeg(a->op_a), TransferNeg(a->op_b));
+ 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.
+ 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);
+ }
+ }
+ // TODO: other cases useful too?
+ }
}
return nullptr;
}
@@ -442,11 +498,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);
+ return CreateInduction(kPeriodic, a, initial, type_);
}
InductionInfo* b = SolvePhi(phi, /* input_index */ 1);
if (b != nullptr && b->induction_class == kPeriodic) {
- return CreateInduction(kPeriodic, a, b);
+ return CreateInduction(kPeriodic, a, b, type_);
}
}
}
@@ -489,7 +545,7 @@
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);
+ return CreateInduction(kPeriodic, CreateInvariantOp(kSub, a, initial), initial, type_);
}
}
}
@@ -497,6 +553,21 @@
return nullptr;
}
+HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::SolveCnv(HTypeConversion* conversion) {
+ Primitive::Type from = conversion->GetInputType();
+ Primitive::Type to = conversion->GetResultType();
+ // A narrowing conversion is allowed within the cycle of a linear induction, provided that the
+ // narrowest encountered type is recorded with the induction to account for the precision loss.
+ if (IsNarrowingIntegralConversion(from, to)) {
+ auto it = cycle_.find(conversion->GetInput());
+ if (it != cycle_.end() && it->second->induction_class == kInvariant) {
+ type_ = Narrowest(type_, to);
+ return it->second;
+ }
+ }
+ return nullptr;
+}
+
void HInductionVarAnalysis::VisitControl(HLoopInformation* loop) {
HInstruction* control = loop->GetHeader()->GetLastInstruction();
if (control->IsIf()) {
@@ -512,12 +583,10 @@
InductionInfo* a = LookupInfo(loop, condition->InputAt(0));
InductionInfo* b = LookupInfo(loop, condition->InputAt(1));
Primitive::Type type = condition->InputAt(0)->GetType();
- // Determine if the loop control uses integral arithmetic and an if-exit (X outside) or an
- // if-iterate (X inside), always expressed as if-iterate when passing into VisitCondition().
- if (type != Primitive::kPrimInt && type != Primitive::kPrimLong) {
- // Loop control is not 32/64-bit integral.
- } else if (a == nullptr || b == nullptr) {
- // Loop control is not a sequence.
+ // Determine if the loop control uses a known sequence on an if-exit (X outside) or on
+ // an if-iterate (X inside), expressed as if-iterate when passed into VisitCondition().
+ if (a == nullptr || b == nullptr) {
+ return; // Loop control is not a sequence.
} else if (if_true->GetLoopInformation() != loop && if_false->GetLoopInformation() == loop) {
VisitCondition(loop, a, b, type, condition->GetOppositeCondition());
} else if (if_true->GetLoopInformation() == loop && if_false->GetLoopInformation() != loop) {
@@ -559,6 +628,14 @@
(stride_value == -1 && IsTaken(lower_expr, upper_expr, kCondGE)))) {
cmp = stride_value > 0 ? kCondLT : kCondGT;
}
+ // Only accept integral condition. A mismatch between the type of condition and the induction
+ // is only allowed if the, necessarily narrower, induction range fits the narrower control.
+ if (type != Primitive::kPrimInt && type != Primitive::kPrimLong) {
+ return; // not integral
+ } else if (type != a->type &&
+ !FitsNarrowerControl(lower_expr, upper_expr, stride_value, a->type, cmp)) {
+ return; // mismatched type
+ }
// 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
@@ -640,7 +717,7 @@
InductionInfo* taken_test = CreateInvariantOp(op, lower_expr, upper_expr);
AssignInfo(loop,
loop->GetHeader()->GetLastInstruction(),
- CreateTripCount(tcKind, trip_count, taken_test));
+ CreateTripCount(tcKind, trip_count, taken_test, type));
}
bool HInductionVarAnalysis::IsTaken(InductionInfo* lower_expr,
@@ -675,10 +752,8 @@
int64_t stride_value,
Primitive::Type type,
IfCondition cmp) {
- const int64_t min = type == Primitive::kPrimInt ? std::numeric_limits<int32_t>::min()
- : std::numeric_limits<int64_t>::min();
- const int64_t max = type == Primitive::kPrimInt ? std::numeric_limits<int32_t>::max()
- : std::numeric_limits<int64_t>::max();
+ const int64_t min = Primitive::MinValueOfIntegralType(type);
+ const int64_t max = Primitive::MaxValueOfIntegralType(type);
// Some rules under which it is certain at compile-time that the loop is finite.
int64_t value;
switch (cmp) {
@@ -698,6 +773,29 @@
return false; // not certain, may be infinite
}
+bool HInductionVarAnalysis::FitsNarrowerControl(InductionInfo* lower_expr,
+ InductionInfo* upper_expr,
+ int64_t stride_value,
+ Primitive::Type type,
+ IfCondition cmp) {
+ int64_t min = Primitive::MinValueOfIntegralType(type);
+ int64_t max = Primitive::MaxValueOfIntegralType(type);
+ // Inclusive test need one extra.
+ if (stride_value != 1 && stride_value != -1) {
+ return false; // non-unit stride
+ } else if (cmp == kCondLE) {
+ max--;
+ } else if (cmp == kCondGE) {
+ min++;
+ }
+ // Do both bounds fit the range?
+ int64_t value;
+ return IsAtLeast(lower_expr, &value) && value >= min &&
+ IsAtMost(lower_expr, &value) && value <= max &&
+ IsAtLeast(upper_expr, &value) && value >= min &&
+ IsAtMost(upper_expr, &value) && value <= max;
+}
+
void HInductionVarAnalysis::AssignInfo(HLoopInformation* loop,
HInstruction* instruction,
InductionInfo* info) {
@@ -794,7 +892,7 @@
return CreateSimplifiedInvariant(kSub, b->op_b, b->op_a);
}
}
- return new (graph_->GetArena()) InductionInfo(kInvariant, op, a, b, nullptr);
+ return new (graph_->GetArena()) InductionInfo(kInvariant, op, a, b, nullptr, b->type);
}
bool HInductionVarAnalysis::IsExact(InductionInfo* info, int64_t* value) {
@@ -856,18 +954,22 @@
case kTripCountInBodyUnsafe: inv += " (TC-body-unsafe) "; break;
}
inv += InductionToString(info->op_b);
- return inv + ")";
+ inv += ")";
+ return inv;
} else {
DCHECK(info->operation == kNop);
if (info->induction_class == kLinear) {
return "(" + InductionToString(info->op_a) + " * i + " +
- InductionToString(info->op_b) + ")";
+ InductionToString(info->op_b) + "):" +
+ Primitive::PrettyDescriptor(info->type);
} else if (info->induction_class == kWrapAround) {
return "wrap(" + InductionToString(info->op_a) + ", " +
- InductionToString(info->op_b) + ")";
+ InductionToString(info->op_b) + "):" +
+ Primitive::PrettyDescriptor(info->type);
} else if (info->induction_class == kPeriodic) {
return "periodic(" + InductionToString(info->op_a) + ", " +
- InductionToString(info->op_b) + ")";
+ InductionToString(info->op_b) + "):" +
+ Primitive::PrettyDescriptor(info->type);
}
}
}
diff --git a/compiler/optimizing/induction_var_analysis.h b/compiler/optimizing/induction_var_analysis.h
index 94d2646..f1965f0 100644
--- a/compiler/optimizing/induction_var_analysis.h
+++ b/compiler/optimizing/induction_var_analysis.h
@@ -97,17 +97,20 @@
InductionOp op,
InductionInfo* a,
InductionInfo* b,
- HInstruction* f)
+ HInstruction* f,
+ Primitive::Type t)
: induction_class(ic),
operation(op),
op_a(a),
op_b(b),
- fetch(f) {}
+ fetch(f),
+ type(t) {}
InductionClass induction_class;
InductionOp operation;
InductionInfo* op_a;
InductionInfo* op_b;
HInstruction* fetch;
+ Primitive::Type type; // precision of induction
};
bool IsVisitedNode(HInstruction* instruction) const {
@@ -121,17 +124,24 @@
InductionInfo* CreateInvariantFetch(HInstruction* f) {
DCHECK(f != nullptr);
- return new (graph_->GetArena()) InductionInfo(kInvariant, kFetch, nullptr, nullptr, f);
+ return new (graph_->GetArena())
+ InductionInfo(kInvariant, kFetch, nullptr, nullptr, f, f->GetType());
}
- InductionInfo* CreateTripCount(InductionOp op, InductionInfo* a, InductionInfo* b) {
+ InductionInfo* CreateTripCount(InductionOp op,
+ InductionInfo* a,
+ InductionInfo* b,
+ Primitive::Type type) {
DCHECK(a != nullptr);
- return new (graph_->GetArena()) InductionInfo(kInvariant, op, a, b, nullptr);
+ return new (graph_->GetArena()) InductionInfo(kInvariant, op, a, b, nullptr, type);
}
- InductionInfo* CreateInduction(InductionClass ic, InductionInfo* a, InductionInfo* b) {
+ InductionInfo* CreateInduction(InductionClass ic,
+ InductionInfo* a,
+ InductionInfo* b,
+ Primitive::Type type) {
DCHECK(a != nullptr && b != nullptr);
- return new (graph_->GetArena()) InductionInfo(ic, kNop, a, b, nullptr);
+ return new (graph_->GetArena()) InductionInfo(ic, kNop, a, b, nullptr, type);
}
// Methods for analysis.
@@ -148,6 +158,7 @@
InductionInfo* TransferMul(InductionInfo* a, InductionInfo* b);
InductionInfo* TransferShl(InductionInfo* a, InductionInfo* b, Primitive::Type type);
InductionInfo* TransferNeg(InductionInfo* a);
+ InductionInfo* TransferCnv(InductionInfo* a, Primitive::Type from, Primitive::Type to);
// Solvers.
InductionInfo* SolvePhi(HInstruction* phi, size_t input_index);
@@ -161,6 +172,7 @@
HInstruction* y,
InductionOp op,
bool is_first_call);
+ InductionInfo* SolveCnv(HTypeConversion* conversion);
// Trip count information.
void VisitControl(HLoopInformation* loop);
@@ -181,6 +193,11 @@
int64_t stride_value,
Primitive::Type type,
IfCondition cmp);
+ bool FitsNarrowerControl(InductionInfo* lower_expr,
+ InductionInfo* upper_expr,
+ int64_t stride_value,
+ Primitive::Type type,
+ IfCondition cmp);
// Assign and lookup.
void AssignInfo(HLoopInformation* loop, HInstruction* instruction, InductionInfo* info);
@@ -205,6 +222,7 @@
ArenaVector<HInstruction*> scc_;
ArenaSafeMap<HInstruction*, NodeInfo> map_;
ArenaSafeMap<HInstruction*, InductionInfo*> cycle_;
+ Primitive::Type type_;
/**
* Maintains the results of the analysis as a mapping from loops to a mapping from instructions
diff --git a/compiler/optimizing/induction_var_analysis_test.cc b/compiler/optimizing/induction_var_analysis_test.cc
index 89e4690..0fbb67d 100644
--- a/compiler/optimizing/induction_var_analysis_test.cc
+++ b/compiler/optimizing/induction_var_analysis_test.cc
@@ -202,6 +202,7 @@
// }
BuildLoopNest(10);
graph_->BuildDominatorTree();
+
ASSERT_EQ(entry_->GetLoopInformation(), nullptr);
for (int d = 0; d < 1; d++) {
ASSERT_EQ(loop_preheader_[d]->GetLoopInformation(),
@@ -224,8 +225,8 @@
HInstruction* store = InsertArrayStore(basic_[0], 0);
PerformInductionVarAnalysis();
- EXPECT_STREQ("((1) * i + (0))", GetInductionInfo(store->InputAt(1), 0).c_str());
- EXPECT_STREQ("((1) * i + (1))", GetInductionInfo(increment_[0], 0).c_str());
+ EXPECT_STREQ("((1) * i + (0)):PrimInt", GetInductionInfo(store->InputAt(1), 0).c_str());
+ EXPECT_STREQ("((1) * i + (1)):PrimInt", GetInductionInfo(increment_[0], 0).c_str());
// Trip-count.
EXPECT_STREQ("((100) (TC-loop) ((0) < (100)))",
@@ -254,11 +255,11 @@
new (&allocator_) HNeg(Primitive::kPrimInt, basic_[0]), 0);
PerformInductionVarAnalysis();
- EXPECT_STREQ("((1) * i + (100))", GetInductionInfo(add, 0).c_str());
- EXPECT_STREQ("(( - (1)) * i + (100))", GetInductionInfo(sub, 0).c_str());
- EXPECT_STREQ("((100) * i + (0))", GetInductionInfo(mul, 0).c_str());
- EXPECT_STREQ("((2) * i + (0))", GetInductionInfo(shl, 0).c_str());
- EXPECT_STREQ("(( - (1)) * i + (0))", GetInductionInfo(neg, 0).c_str());
+ EXPECT_STREQ("((1) * i + (100)):PrimInt", GetInductionInfo(add, 0).c_str());
+ EXPECT_STREQ("(( - (1)) * i + (100)):PrimInt", GetInductionInfo(sub, 0).c_str());
+ EXPECT_STREQ("((100) * i + (0)):PrimInt", GetInductionInfo(mul, 0).c_str());
+ EXPECT_STREQ("((2) * i + (0)):PrimInt", GetInductionInfo(shl, 0).c_str());
+ EXPECT_STREQ("(( - (1)) * i + (0)):PrimInt", GetInductionInfo(neg, 0).c_str());
}
TEST_F(InductionVarAnalysisTest, FindChainInduction) {
@@ -283,9 +284,9 @@
k->AddInput(sub);
PerformInductionVarAnalysis();
- EXPECT_STREQ("(((100) - (1)) * i + (100))",
+ EXPECT_STREQ("(((100) - (1)) * i + (100)):PrimInt",
GetInductionInfo(store1->InputAt(1), 0).c_str());
- EXPECT_STREQ("(((100) - (1)) * i + ((100) - (1)))",
+ EXPECT_STREQ("(((100) - (1)) * i + ((100) - (1))):PrimInt",
GetInductionInfo(store2->InputAt(1), 0).c_str());
}
@@ -318,7 +319,7 @@
k_header->AddInput(k_body);
PerformInductionVarAnalysis();
- EXPECT_STREQ("((1) * i + (1))", GetInductionInfo(store->InputAt(1), 0).c_str());
+ EXPECT_STREQ("((1) * i + (1)):PrimInt", GetInductionInfo(store->InputAt(1), 0).c_str());
}
TEST_F(InductionVarAnalysisTest, FindTwoWayDerivedInduction) {
@@ -345,7 +346,7 @@
HInstruction* store = InsertArrayStore(k, 0);
PerformInductionVarAnalysis();
- EXPECT_STREQ("((1) * i + (1))", GetInductionInfo(store->InputAt(1), 0).c_str());
+ EXPECT_STREQ("((1) * i + (1)):PrimInt", GetInductionInfo(store->InputAt(1), 0).c_str());
}
TEST_F(InductionVarAnalysisTest, FindFirstOrderWrapAroundInduction) {
@@ -365,7 +366,7 @@
k->AddInput(sub);
PerformInductionVarAnalysis();
- EXPECT_STREQ("wrap((0), (( - (1)) * i + (100)))",
+ EXPECT_STREQ("wrap((0), (( - (1)) * i + (100)):PrimInt):PrimInt",
GetInductionInfo(store->InputAt(1), 0).c_str());
}
@@ -391,7 +392,7 @@
t->AddInput(sub);
PerformInductionVarAnalysis();
- EXPECT_STREQ("wrap((0), wrap((100), (( - (1)) * i + (100))))",
+ EXPECT_STREQ("wrap((0), wrap((100), (( - (1)) * i + (100)):PrimInt):PrimInt):PrimInt",
GetInductionInfo(store->InputAt(1), 0).c_str());
}
@@ -424,11 +425,16 @@
InsertInstruction(new (&allocator_) HShl(Primitive::kPrimInt, basic_[0], constant1_), 0));
PerformInductionVarAnalysis();
- EXPECT_STREQ("wrap((100), ((2) * i + (100)))", GetInductionInfo(add, 0).c_str());
- EXPECT_STREQ("wrap(((0) - (100)), ((2) * i + ((0) - (100))))", GetInductionInfo(sub, 0).c_str());
- EXPECT_STREQ("wrap((0), (((2) * (100)) * i + (0)))", GetInductionInfo(mul, 0).c_str());
- EXPECT_STREQ("wrap((0), (((2) * (2)) * i + (0)))", GetInductionInfo(shl, 0).c_str());
- EXPECT_STREQ("wrap((0), (( - (2)) * i + (0)))", GetInductionInfo(neg, 0).c_str());
+ EXPECT_STREQ("wrap((100), ((2) * i + (100)):PrimInt):PrimInt",
+ GetInductionInfo(add, 0).c_str());
+ EXPECT_STREQ("wrap(((0) - (100)), ((2) * i + ((0) - (100))):PrimInt):PrimInt",
+ GetInductionInfo(sub, 0).c_str());
+ 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());
+ EXPECT_STREQ("wrap((0), (( - (2)) * i + (0)):PrimInt):PrimInt",
+ GetInductionInfo(neg, 0).c_str());
}
TEST_F(InductionVarAnalysisTest, FindPeriodicInduction) {
@@ -455,8 +461,8 @@
t->AddInput(k);
PerformInductionVarAnalysis();
- EXPECT_STREQ("periodic((0), (100))", GetInductionInfo(store1->InputAt(1), 0).c_str());
- EXPECT_STREQ("periodic((100), (0))", GetInductionInfo(store2->InputAt(1), 0).c_str());
+ EXPECT_STREQ("periodic((0), (100)):PrimInt", GetInductionInfo(store1->InputAt(1), 0).c_str());
+ EXPECT_STREQ("periodic((100), (0)):PrimInt", GetInductionInfo(store2->InputAt(1), 0).c_str());
}
TEST_F(InductionVarAnalysisTest, FindIdiomaticPeriodicInduction) {
@@ -476,8 +482,8 @@
k->AddInput(sub);
PerformInductionVarAnalysis();
- EXPECT_STREQ("periodic((0), (1))", GetInductionInfo(store->InputAt(1), 0).c_str());
- EXPECT_STREQ("periodic((1), (0))", GetInductionInfo(sub, 0).c_str());
+ EXPECT_STREQ("periodic((0), (1)):PrimInt", GetInductionInfo(store->InputAt(1), 0).c_str());
+ EXPECT_STREQ("periodic((1), (0)):PrimInt", GetInductionInfo(sub, 0).c_str());
}
TEST_F(InductionVarAnalysisTest, FindDerivedPeriodicInduction) {
@@ -512,11 +518,11 @@
new (&allocator_) HNeg(Primitive::kPrimInt, k_body), 0);
PerformInductionVarAnalysis();
- EXPECT_STREQ("periodic(((1) + (100)), (100))", GetInductionInfo(add, 0).c_str());
- EXPECT_STREQ("periodic(((1) - (100)), ((0) - (100)))", GetInductionInfo(sub, 0).c_str());
- EXPECT_STREQ("periodic((100), (0))", GetInductionInfo(mul, 0).c_str());
- EXPECT_STREQ("periodic((2), (0))", GetInductionInfo(shl, 0).c_str());
- EXPECT_STREQ("periodic(( - (1)), (0))", GetInductionInfo(neg, 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());
}
TEST_F(InductionVarAnalysisTest, FindDeepLoopInduction) {
@@ -549,7 +555,7 @@
// Avoid exact phi number, since that depends on the SSA building phase.
std::regex r("\\(\\(1\\) \\* i \\+ "
- "\\(\\(1\\) \\+ \\(\\d+:Phi\\)\\)\\)");
+ "\\(\\(1\\) \\+ \\(\\d+:Phi\\)\\)\\):PrimInt");
for (int d = 0; d < 10; d++) {
if (d == 9) {
@@ -557,11 +563,122 @@
} else {
EXPECT_STREQ("", GetInductionInfo(store->InputAt(1), d).c_str());
}
- EXPECT_STREQ("((1) * i + (1))", GetInductionInfo(increment_[d], d).c_str());
+ EXPECT_STREQ("((1) * i + (1)):PrimInt", GetInductionInfo(increment_[d], d).c_str());
// Trip-count.
EXPECT_STREQ("((100) (TC-loop) ((0) < (100)))",
GetInductionInfo(loop_header_[d]->GetLastInstruction(), d).c_str());
}
}
+TEST_F(InductionVarAnalysisTest, ByteLoopControl1) {
+ // Setup:
+ // for (byte i = -128; i < 127; i++) { // just fits!
+ // }
+ BuildLoopNest(1);
+ basic_[0]->ReplaceInput(graph_->GetIntConstant(-128), 0);
+ HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious();
+ ifs->ReplaceInput(graph_->GetIntConstant(127), 1);
+ HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimByte, increment_[0], -1);
+ loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
+ basic_[0]->ReplaceInput(conv, 1);
+ PerformInductionVarAnalysis();
+
+ EXPECT_STREQ("((1) * i + ((-128) + (1))):PrimByte", GetInductionInfo(increment_[0], 0).c_str());
+ // Trip-count.
+ EXPECT_STREQ("(((127) - (-128)) (TC-loop) ((-128) < (127)))",
+ GetInductionInfo(loop_header_[0]->GetLastInstruction(), 0).c_str());
+}
+
+TEST_F(InductionVarAnalysisTest, ByteLoopControl2) {
+ // Setup:
+ // for (byte i = -128; i < 128; i++) { // infinite loop!
+ // }
+ BuildLoopNest(1);
+ basic_[0]->ReplaceInput(graph_->GetIntConstant(-128), 0);
+ HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious();
+ ifs->ReplaceInput(graph_->GetIntConstant(128), 1);
+ HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimByte, increment_[0], -1);
+ loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
+ basic_[0]->ReplaceInput(conv, 1);
+ PerformInductionVarAnalysis();
+
+ EXPECT_STREQ("((1) * i + ((-128) + (1))):PrimByte", GetInductionInfo(increment_[0], 0).c_str());
+ // Trip-count undefined.
+ EXPECT_STREQ("", GetInductionInfo(loop_header_[0]->GetLastInstruction(), 0).c_str());
+}
+
+TEST_F(InductionVarAnalysisTest, ShortLoopControl1) {
+ // Setup:
+ // for (short i = -32768; i < 32767; i++) { // just fits!
+ // }
+ BuildLoopNest(1);
+ basic_[0]->ReplaceInput(graph_->GetIntConstant(-32768), 0);
+ HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious();
+ ifs->ReplaceInput(graph_->GetIntConstant(32767), 1);
+ HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimShort, increment_[0], -1);
+ loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
+ basic_[0]->ReplaceInput(conv, 1);
+ PerformInductionVarAnalysis();
+
+ EXPECT_STREQ("((1) * i + ((-32768) + (1))):PrimShort",
+ GetInductionInfo(increment_[0], 0).c_str());
+ // Trip-count.
+ EXPECT_STREQ("(((32767) - (-32768)) (TC-loop) ((-32768) < (32767)))",
+ GetInductionInfo(loop_header_[0]->GetLastInstruction(), 0).c_str());
+}
+
+TEST_F(InductionVarAnalysisTest, ShortLoopControl2) {
+ // Setup:
+ // for (short i = -32768; i < 32768; i++) { // infinite loop!
+ // }
+ BuildLoopNest(1);
+ basic_[0]->ReplaceInput(graph_->GetIntConstant(-32768), 0);
+ HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious();
+ ifs->ReplaceInput(graph_->GetIntConstant(32768), 1);
+ HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimShort, increment_[0], -1);
+ loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
+ basic_[0]->ReplaceInput(conv, 1);
+ PerformInductionVarAnalysis();
+
+ EXPECT_STREQ("((1) * i + ((-32768) + (1))):PrimShort",
+ GetInductionInfo(increment_[0], 0).c_str());
+ // Trip-count undefined.
+ EXPECT_STREQ("", GetInductionInfo(loop_header_[0]->GetLastInstruction(), 0).c_str());
+}
+
+TEST_F(InductionVarAnalysisTest, CharLoopControl1) {
+ // Setup:
+ // for (char i = 0; i < 65535; i++) { // just fits!
+ // }
+ BuildLoopNest(1);
+ HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious();
+ ifs->ReplaceInput(graph_->GetIntConstant(65535), 1);
+ HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimChar, increment_[0], -1);
+ loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
+ basic_[0]->ReplaceInput(conv, 1);
+ PerformInductionVarAnalysis();
+
+ EXPECT_STREQ("((1) * i + (1)):PrimChar", GetInductionInfo(increment_[0], 0).c_str());
+ // Trip-count.
+ EXPECT_STREQ("((65535) (TC-loop) ((0) < (65535)))",
+ GetInductionInfo(loop_header_[0]->GetLastInstruction(), 0).c_str());
+}
+
+TEST_F(InductionVarAnalysisTest, CharLoopControl2) {
+ // Setup:
+ // for (char i = 0; i < 65536; i++) { // infinite loop!
+ // }
+ BuildLoopNest(1);
+ HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious();
+ ifs->ReplaceInput(graph_->GetIntConstant(65536), 1);
+ HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimChar, increment_[0], -1);
+ loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
+ basic_[0]->ReplaceInput(conv, 1);
+ PerformInductionVarAnalysis();
+
+ EXPECT_STREQ("((1) * i + (1)):PrimChar", GetInductionInfo(increment_[0], 0).c_str());
+ // Trip-count undefined.
+ EXPECT_STREQ("", GetInductionInfo(loop_header_[0]->GetLastInstruction(), 0).c_str());
+}
+
} // namespace art
diff --git a/compiler/optimizing/induction_var_range.cc b/compiler/optimizing/induction_var_range.cc
index f9b6910..bc920d9 100644
--- a/compiler/optimizing/induction_var_range.cc
+++ b/compiler/optimizing/induction_var_range.cc
@@ -58,13 +58,13 @@
}
/**
- * An upper bound a * (length / a) + b, where a > 0, can be conservatively rewritten as length + b
+ * An upper bound a * (length / a) + b, where a >= 1, can be conservatively rewritten as length + b
* because length >= 0 is true. This makes it more likely the bound is useful to clients.
*/
static InductionVarRange::Value SimplifyMax(InductionVarRange::Value v) {
int64_t value;
if (v.is_known &&
- v.a_constant > 1 &&
+ v.a_constant >= 1 &&
v.instruction->IsDiv() &&
v.instruction->InputAt(0)->IsArrayLength() &&
IsIntAndGet(v.instruction->InputAt(1), &value) && v.a_constant == value) {
@@ -73,6 +73,28 @@
return v;
}
+/**
+ * Corrects a value for type to account for arithmetic wrap-around in lower precision.
+ */
+static InductionVarRange::Value CorrectForType(InductionVarRange::Value v, Primitive::Type type) {
+ switch (type) {
+ case Primitive::kPrimShort:
+ case Primitive::kPrimChar:
+ case Primitive::kPrimByte: {
+ // Constants within range only.
+ // TODO: maybe some room for improvement, like allowing widening conversions
+ const int32_t min = Primitive::MinValueOfIntegralType(type);
+ const int32_t max = Primitive::MaxValueOfIntegralType(type);
+ return (v.is_known && v.a_constant == 0 && min <= v.b_constant && v.b_constant <= max)
+ ? v
+ : InductionVarRange::Value();
+ }
+ default:
+ // At int or higher.
+ return v;
+ }
+}
+
/** Helper method to test for a constant value. */
static bool IsConstantValue(InductionVarRange::Value v) {
return v.is_known && v.a_constant == 0;
@@ -114,6 +136,18 @@
if (info == nullptr) {
return false; // no induction information
}
+ // Type int or lower (this is not too restrictive since intended clients, like
+ // bounds check elimination, will have truncated higher precision induction
+ // at their use point already).
+ switch (info->type) {
+ case Primitive::kPrimInt:
+ case Primitive::kPrimShort:
+ case Primitive::kPrimChar:
+ case Primitive::kPrimByte:
+ break;
+ default:
+ return false;
+ }
// Set up loop information.
HBasicBlock* header = loop->GetHeader();
bool in_body = context->GetBlock() != header;
@@ -128,25 +162,27 @@
bool InductionVarRange::RefineOuter(/*in-out*/ Value* min_val,
/*in-out*/ Value* max_val) const {
- Value v1_min = RefineOuter(*min_val, /* is_min */ true);
- Value v2_max = RefineOuter(*max_val, /* is_min */ false);
- // The refined range is safe if both sides refine the same instruction. Otherwise, since two
- // different ranges are combined, the new refined range is safe to pass back to the client if
- // the extremes of the computed ranges ensure no arithmetic wrap-around anomalies occur.
- if (min_val->instruction != max_val->instruction) {
- Value v1_max = RefineOuter(*min_val, /* is_min */ false);
- Value v2_min = RefineOuter(*max_val, /* is_min */ true);
- if (!IsConstantValue(v1_max) ||
- !IsConstantValue(v2_min) ||
- v1_max.b_constant > v2_min.b_constant) {
- return false;
+ if (min_val->instruction != nullptr || max_val->instruction != nullptr) {
+ Value v1_min = RefineOuter(*min_val, /* is_min */ true);
+ Value v2_max = RefineOuter(*max_val, /* is_min */ false);
+ // The refined range is safe if both sides refine the same instruction. Otherwise, since two
+ // different ranges are combined, the new refined range is safe to pass back to the client if
+ // the extremes of the computed ranges ensure no arithmetic wrap-around anomalies occur.
+ if (min_val->instruction != max_val->instruction) {
+ Value v1_max = RefineOuter(*min_val, /* is_min */ false);
+ Value v2_min = RefineOuter(*max_val, /* is_min */ true);
+ if (!IsConstantValue(v1_max) ||
+ !IsConstantValue(v2_min) ||
+ v1_max.b_constant > v2_min.b_constant) {
+ return false;
+ }
}
- }
- // Did something change?
- if (v1_min.instruction != min_val->instruction || v2_max.instruction != max_val->instruction) {
- *min_val = v1_min;
- *max_val = v2_max;
- return true;
+ // Did something change?
+ if (v1_min.instruction != min_val->instruction || v2_max.instruction != max_val->instruction) {
+ *min_val = v1_min;
+ *max_val = v2_max;
+ return true;
+ }
}
return false;
}
@@ -277,7 +313,12 @@
if (HInductionVarAnalysis::InductionEqual(trip_expr->op_b, info->op_b)) {
// Analyze cancelled trip with just the positive operand (trip_expr->op_a).
HInductionVarAnalysis::InductionInfo cancelled_trip(
- trip->induction_class, trip->operation, trip_expr->op_a, trip->op_b, nullptr);
+ trip->induction_class,
+ trip->operation,
+ trip_expr->op_a,
+ trip->op_b,
+ nullptr,
+ trip->type);
return GetVal(&cancelled_trip, trip, in_body, is_min);
}
} else if (is_min && stride_value == -1) {
@@ -289,9 +330,10 @@
HInductionVarAnalysis::kNeg,
nullptr,
trip_expr->op_b,
- nullptr);
+ nullptr,
+ trip->type);
HInductionVarAnalysis::InductionInfo cancelled_trip(
- trip->induction_class, trip->operation, &neg, trip->op_b, nullptr);
+ trip->induction_class, trip->operation, &neg, trip->op_b, nullptr, trip->type);
return SubValue(Value(0), GetVal(&cancelled_trip, trip, in_body, !is_min));
}
}
@@ -322,6 +364,12 @@
}
} else if (instruction->IsArrayLength() && instruction->InputAt(0)->IsNewArray()) {
return GetFetch(instruction->InputAt(0)->InputAt(0), trip, in_body, is_min);
+ } else if (instruction->IsTypeConversion()) {
+ // Since analysis is 32-bit (or narrower) we allow a widening along the path.
+ if (instruction->AsTypeConversion()->GetInputType() == Primitive::kPrimInt &&
+ instruction->AsTypeConversion()->GetResultType() == Primitive::kPrimLong) {
+ return GetFetch(instruction->InputAt(0), trip, in_body, is_min);
+ }
} else if (is_min) {
// Special case for finding minimum: minimum of trip-count in loop-body is 1.
if (trip != nullptr && in_body && instruction == trip->op_a->fetch) {
@@ -374,7 +422,7 @@
}
break;
case HInductionVarAnalysis::kLinear: {
- return GetLinear(info, trip, in_body, is_min);
+ return CorrectForType(GetLinear(info, trip, in_body, is_min), info->type);
}
case HInductionVarAnalysis::kWrapAround:
case HInductionVarAnalysis::kPeriodic:
@@ -613,8 +661,12 @@
bool in_body,
bool is_min) const {
if (info != nullptr) {
- // Handle current operation.
+ // Verify type safety.
Primitive::Type type = Primitive::kPrimInt;
+ if (info->type != type) {
+ return false;
+ }
+ // Handle current operation.
HInstruction* opa = nullptr;
HInstruction* opb = nullptr;
switch (info->induction_class) {
@@ -667,13 +719,10 @@
}
break;
case HInductionVarAnalysis::kFetch:
- if (info->fetch->GetType() == type) {
- if (graph != nullptr) {
- *result = info->fetch; // already in HIR
- }
- return true;
+ if (graph != nullptr) {
+ *result = info->fetch; // already in HIR
}
- break;
+ return true;
case HInductionVarAnalysis::kTripCountInLoop:
case HInductionVarAnalysis::kTripCountInLoopUnsafe:
if (!in_body && !is_min) { // one extra!
diff --git a/compiler/optimizing/induction_var_range_test.cc b/compiler/optimizing/induction_var_range_test.cc
index c5c33bd..dc04dc2 100644
--- a/compiler/optimizing/induction_var_range_test.cc
+++ b/compiler/optimizing/induction_var_range_test.cc
@@ -139,37 +139,40 @@
/** Constructs a trip-count. */
HInductionVarAnalysis::InductionInfo* CreateTripCount(int32_t tc, bool in_loop, bool safe) {
+ Primitive::Type type = Primitive::kPrimInt;
if (in_loop && safe) {
return iva_->CreateTripCount(
- HInductionVarAnalysis::kTripCountInLoop, CreateConst(tc), nullptr);
+ HInductionVarAnalysis::kTripCountInLoop, CreateConst(tc), nullptr, type);
} else if (in_loop) {
return iva_->CreateTripCount(
- HInductionVarAnalysis::kTripCountInLoopUnsafe, CreateConst(tc), nullptr);
+ HInductionVarAnalysis::kTripCountInLoopUnsafe, CreateConst(tc), nullptr, type);
} else if (safe) {
return iva_->CreateTripCount(
- HInductionVarAnalysis::kTripCountInBody, CreateConst(tc), nullptr);
+ HInductionVarAnalysis::kTripCountInBody, CreateConst(tc), nullptr, type);
} else {
return iva_->CreateTripCount(
- HInductionVarAnalysis::kTripCountInBodyUnsafe, CreateConst(tc), nullptr);
+ HInductionVarAnalysis::kTripCountInBodyUnsafe, CreateConst(tc), nullptr, type);
}
}
/** 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));
+ return iva_->CreateInduction(
+ HInductionVarAnalysis::kLinear, CreateConst(a), CreateConst(b), 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));
+ HInductionVarAnalysis::kPeriodic, CreateConst(lo), CreateConst(hi), 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);
+ return iva_->CreateInduction(
+ HInductionVarAnalysis::kWrapAround, CreateConst(initial), info, Primitive::kPrimInt);
}
/** Constructs a wrap-around induction consisting of a constant, followed by a range. */
diff --git a/runtime/Android.mk b/runtime/Android.mk
index f70e696..fc96acf 100644
--- a/runtime/Android.mk
+++ b/runtime/Android.mk
@@ -368,6 +368,7 @@
mirror/class.h \
oat.h \
object_callbacks.h \
+ process_state.h \
profiler_options.h \
quick/inline_method_analyser.h \
runtime.h \
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 2b43dfb..c1115da 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -760,7 +760,8 @@
const std::vector<gc::space::ImageSpace*>& spaces)
SHARED_REQUIRES(Locks::mutator_lock_) {
if (m->IsRuntimeMethod()) {
- CHECK(m->GetDeclaringClass() == nullptr) << PrettyMethod(m);
+ mirror::Class* declaring_class = m->GetDeclaringClassUnchecked();
+ CHECK(declaring_class == nullptr) << declaring_class << " " << PrettyMethod(m);
} else if (m->IsCopied()) {
CHECK(m->GetDeclaringClass() != nullptr) << PrettyMethod(m);
} else if (expected_class != nullptr) {
diff --git a/runtime/gc/collector/mark_sweep.cc b/runtime/gc/collector/mark_sweep.cc
index 6073fc8..894ceba 100644
--- a/runtime/gc/collector/mark_sweep.cc
+++ b/runtime/gc/collector/mark_sweep.cc
@@ -845,7 +845,9 @@
};
size_t MarkSweep::GetThreadCount(bool paused) const {
- if (heap_->GetThreadPool() == nullptr || !heap_->CareAboutPauseTimes()) {
+ // Use less threads if we are in a background state (non jank perceptible) since we want to leave
+ // more CPU time for the foreground apps.
+ if (heap_->GetThreadPool() == nullptr || !Runtime::Current()->InJankPerceptibleProcessState()) {
return 1;
}
return (paused ? heap_->GetParallelGCThreadCount() : heap_->GetConcGCThreadCount()) + 1;
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 4ff0c6b..a96847f 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -117,6 +117,10 @@
// For deterministic compilation, we need the heap to be at a well-known address.
static constexpr uint32_t kAllocSpaceBeginForDeterministicAoT = 0x40000000;
+static inline bool CareAboutPauseTimes() {
+ return Runtime::Current()->InJankPerceptibleProcessState();
+}
+
Heap::Heap(size_t initial_size,
size_t growth_limit,
size_t min_free,
@@ -175,8 +179,6 @@
max_allowed_footprint_(initial_size),
native_footprint_gc_watermark_(initial_size),
native_need_to_run_finalization_(false),
- // Initially assume we perceive jank in case the process state is never updated.
- process_state_(kProcessStateJankPerceptible),
concurrent_start_bytes_(std::numeric_limits<size_t>::max()),
total_bytes_freed_ever_(0),
total_objects_freed_ever_(0),
@@ -924,17 +926,18 @@
thread_flip_cond_->Broadcast(self);
}
-void Heap::UpdateProcessState(ProcessState process_state) {
- if (process_state_ != process_state) {
- process_state_ = process_state;
+void Heap::UpdateProcessState(ProcessState old_process_state, ProcessState new_process_state) {
+ if (old_process_state != new_process_state) {
+ const bool jank_perceptible = new_process_state == kProcessStateJankPerceptible;
for (size_t i = 1; i <= kCollectorTransitionStressIterations; ++i) {
// Start at index 1 to avoid "is always false" warning.
// Have iteration 1 always transition the collector.
- TransitionCollector((((i & 1) == 1) == (process_state_ == kProcessStateJankPerceptible))
- ? foreground_collector_type_ : background_collector_type_);
+ TransitionCollector((((i & 1) == 1) == jank_perceptible)
+ ? foreground_collector_type_
+ : background_collector_type_);
usleep(kCollectorTransitionStressWait);
}
- if (process_state_ == kProcessStateJankPerceptible) {
+ if (jank_perceptible) {
// Transition back to foreground right away to prevent jank.
RequestCollectorTransition(foreground_collector_type_, 0);
} else {
@@ -2204,8 +2207,8 @@
} else {
saved_str = " expanded " + PrettySize(-delta_allocated);
}
- VLOG(heap) << "Heap transition to " << process_state_ << " took "
- << PrettyDuration(duration) << saved_str;
+ VLOG(heap) << "Collector transition to " << collector_type << " took "
+ << PrettyDuration(duration) << saved_str;
}
void Heap::ChangeCollector(CollectorType collector_type) {
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index 9eda422..2925591 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -36,6 +36,7 @@
#include "globals.h"
#include "object_callbacks.h"
#include "offsets.h"
+#include "process_state.h"
#include "safe_map.h"
#include "verify_object.h"
@@ -116,14 +117,6 @@
// If true, use thread-local allocation stack.
static constexpr bool kUseThreadLocalAllocationStack = true;
-// The process state passed in from the activity manager, used to determine when to do trimming
-// and compaction.
-enum ProcessState {
- kProcessStateJankPerceptible = 0,
- kProcessStateJankImperceptible = 1,
-};
-std::ostream& operator<<(std::ostream& os, const ProcessState& process_state);
-
class Heap {
public:
// If true, measure the total allocation time.
@@ -382,7 +375,7 @@
collector::GcType WaitForGcToComplete(GcCause cause, Thread* self) REQUIRES(!*gc_complete_lock_);
// Update the heap's process state to a new value, may cause compaction to occur.
- void UpdateProcessState(ProcessState process_state)
+ void UpdateProcessState(ProcessState old_process_state, ProcessState new_process_state)
REQUIRES(!*pending_task_lock_, !*gc_complete_lock_);
bool HaveContinuousSpaces() const NO_THREAD_SAFETY_ANALYSIS {
@@ -664,11 +657,6 @@
void DumpGcPerformanceInfo(std::ostream& os) REQUIRES(!*gc_complete_lock_);
void ResetGcPerformanceInfo() REQUIRES(!*gc_complete_lock_);
- // Returns true if we currently care about pause times.
- bool CareAboutPauseTimes() const {
- return process_state_ == kProcessStateJankPerceptible;
- }
-
// Thread pool.
void CreateThreadPool();
void DeleteThreadPool();
@@ -1152,9 +1140,6 @@
// Whether or not we need to run finalizers in the next native allocation.
bool native_need_to_run_finalization_;
- // Whether or not we currently care about pause times.
- ProcessState process_state_;
-
// When num_bytes_allocated_ exceeds this amount then a concurrent GC should be requested so that
// it completes ahead of an allocation failing.
size_t concurrent_start_bytes_;
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index 5ff1cb7..22bf5f9 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -1520,6 +1520,17 @@
/*out*/error_msg);
}
+void ImageSpace::DumpSections(std::ostream& os) const {
+ const uint8_t* base = Begin();
+ const ImageHeader& header = GetImageHeader();
+ for (size_t i = 0; i < ImageHeader::kSectionCount; ++i) {
+ auto section_type = static_cast<ImageHeader::ImageSections>(i);
+ const ImageSection& section = header.GetImageSection(section_type);
+ os << section_type << " " << reinterpret_cast<const void*>(base + section.Offset())
+ << "-" << reinterpret_cast<const void*>(base + section.End()) << "\n";
+ }
+}
+
} // namespace space
} // namespace gc
} // namespace art
diff --git a/runtime/gc/space/image_space.h b/runtime/gc/space/image_space.h
index f2f4163..c9741d0 100644
--- a/runtime/gc/space/image_space.h
+++ b/runtime/gc/space/image_space.h
@@ -149,6 +149,8 @@
return GetImageHeader().GetOatFileEnd();
}
+ void DumpSections(std::ostream& os) const;
+
protected:
// Tries to initialize an ImageSpace from the given image path, returning null on error.
//
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index 19584ed..dfb728f 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -388,8 +388,6 @@
}
return false;
}
- DCHECK_EQ(this->CanAccessMember(access_to, field->GetAccessFlags()),
- this->CanAccessMember(dex_access_to, field->GetAccessFlags()));
}
if (LIKELY(this->CanAccessMember(access_to, field->GetAccessFlags()))) {
return true;
diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc
index 4ac28ae..d22c0c7 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -223,7 +223,7 @@
static void VMRuntime_updateProcessState(JNIEnv*, jobject, jint process_state) {
Runtime* runtime = Runtime::Current();
- runtime->GetHeap()->UpdateProcessState(static_cast<gc::ProcessState>(process_state));
+ runtime->UpdateProcessState(static_cast<ProcessState>(process_state));
}
static void VMRuntime_trimHeap(JNIEnv* env, jobject) {
diff --git a/runtime/primitive.h b/runtime/primitive.h
index 9c19ad5..18f45ff 100644
--- a/runtime/primitive.h
+++ b/runtime/primitive.h
@@ -180,6 +180,46 @@
}
}
+ static int64_t MinValueOfIntegralType(Type type) {
+ switch (type) {
+ case kPrimBoolean:
+ return std::numeric_limits<bool>::min();
+ case kPrimByte:
+ return std::numeric_limits<int8_t>::min();
+ case kPrimChar:
+ return std::numeric_limits<uint16_t>::min();
+ case kPrimShort:
+ return std::numeric_limits<int16_t>::min();
+ case kPrimInt:
+ return std::numeric_limits<int32_t>::min();
+ case kPrimLong:
+ return std::numeric_limits<int64_t>::min();
+ default:
+ LOG(FATAL) << "non integral type";
+ }
+ return 0;
+ }
+
+ static int64_t MaxValueOfIntegralType(Type type) {
+ switch (type) {
+ case kPrimBoolean:
+ return std::numeric_limits<bool>::max();
+ case kPrimByte:
+ return std::numeric_limits<int8_t>::max();
+ case kPrimChar:
+ return std::numeric_limits<uint16_t>::max();
+ case kPrimShort:
+ return std::numeric_limits<int16_t>::max();
+ case kPrimInt:
+ return std::numeric_limits<int32_t>::max();
+ case kPrimLong:
+ return std::numeric_limits<int64_t>::max();
+ default:
+ LOG(FATAL) << "non integral type";
+ }
+ return 0;
+ }
+
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(Primitive);
};
diff --git a/runtime/process_state.h b/runtime/process_state.h
new file mode 100644
index 0000000..e8797d6
--- /dev/null
+++ b/runtime/process_state.h
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_RUNTIME_PROCESS_STATE_H_
+#define ART_RUNTIME_PROCESS_STATE_H_
+
+namespace art {
+
+// The process state passed in from the activity manager, used to determine when to do trimming
+// and compaction.
+enum ProcessState {
+ kProcessStateJankPerceptible = 0,
+ kProcessStateJankImperceptible = 1,
+};
+
+std::ostream& operator<<(std::ostream& os, const ProcessState& process_state);
+
+} // namespace art
+
+#endif // ART_RUNTIME_PROCESS_STATE_H_
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 5284c93..daeb447 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -209,7 +209,9 @@
oat_file_manager_(nullptr),
is_low_memory_mode_(false),
safe_mode_(false),
- pruned_dalvik_cache_(false) {
+ pruned_dalvik_cache_(false),
+ // Initially assume we perceive jank in case the process state is never updated.
+ process_state_(kProcessStateJankPerceptible) {
CheckAsmSupportOffsetsAndSizes();
std::fill(callee_save_methods_, callee_save_methods_ + arraysize(callee_save_methods_), 0u);
interpreter::CheckInterpreterAsmConstants();
@@ -1955,4 +1957,10 @@
return is_low_memory_mode_ ? kLowMemoryMaxLoadFactor : kNormalMaxLoadFactor;
}
+void Runtime::UpdateProcessState(ProcessState process_state) {
+ ProcessState old_process_state = process_state_;
+ process_state_ = process_state;
+ GetHeap()->UpdateProcessState(old_process_state, process_state);
+}
+
} // namespace art
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 36ad7f1..ac6e689 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -35,6 +35,7 @@
#include "method_reference.h"
#include "object_callbacks.h"
#include "offsets.h"
+#include "process_state.h"
#include "profiler_options.h"
#include "quick/quick_method_frame_info.h"
#include "runtime_stats.h"
@@ -626,6 +627,13 @@
pruned_dalvik_cache_ = pruned;
}
+ void UpdateProcessState(ProcessState process_state);
+
+ // Returns true if we currently care about long mutator pause.
+ bool InJankPerceptibleProcessState() const {
+ return process_state_ == kProcessStateJankPerceptible;
+ }
+
private:
static void InitPlatformSignalHandlers();
@@ -844,6 +852,9 @@
// Whether the dalvik cache was pruned when initializing the runtime.
bool pruned_dalvik_cache_;
+ // Whether or not we currently care about pause times.
+ ProcessState process_state_;
+
DISALLOW_COPY_AND_ASSIGN(Runtime);
};
std::ostream& operator<<(std::ostream& os, const Runtime::CalleeSaveType& rhs);
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 2a7cd07..4dc6d57 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -46,7 +46,7 @@
#include "gc/accounting/card_table-inl.h"
#include "gc/allocator/rosalloc.h"
#include "gc/heap.h"
-#include "gc/space/space.h"
+#include "gc/space/space-inl.h"
#include "handle_scope-inl.h"
#include "indirect_reference_table-inl.h"
#include "jni_internal.h"
@@ -90,6 +90,8 @@
pthread_key_t Thread::pthread_key_self_;
ConditionVariable* Thread::resume_cond_ = nullptr;
const size_t Thread::kStackOverflowImplicitCheckSize = GetStackOverflowReservedBytes(kRuntimeISA);
+// Enabled for b/27493510. TODO: disable when fixed.
+static constexpr bool kVerifyImageObjectsMarked = true;
// For implicit overflow checks we reserve an extra piece of memory at the bottom
// of the stack (lowest memory). The higher portion of the memory
@@ -2716,11 +2718,37 @@
private:
// Visiting the declaring class is necessary so that we don't unload the class of a method that
- // is executing. We need to ensure that the code stays mapped.
- void VisitDeclaringClass(ArtMethod* method) SHARED_REQUIRES(Locks::mutator_lock_) {
+ // is executing. We need to ensure that the code stays mapped. NO_THREAD_SAFETY_ANALYSIS since
+ // the threads do not all hold the heap bitmap lock for parallel GC.
+ void VisitDeclaringClass(ArtMethod* method)
+ SHARED_REQUIRES(Locks::mutator_lock_)
+ NO_THREAD_SAFETY_ANALYSIS {
mirror::Class* klass = method->GetDeclaringClassUnchecked<kWithoutReadBarrier>();
// klass can be null for runtime methods.
if (klass != nullptr) {
+ if (kVerifyImageObjectsMarked) {
+ gc::Heap* const heap = Runtime::Current()->GetHeap();
+ gc::space::ContinuousSpace* space = heap->FindContinuousSpaceFromObject(klass,
+ /*fail_ok*/true);
+ if (space != nullptr && space->IsImageSpace()) {
+ bool failed = false;
+ if (!space->GetLiveBitmap()->Test(klass)) {
+ failed = true;
+ LOG(INTERNAL_FATAL) << "Unmarked object in image " << *space;
+ } else if (!heap->GetLiveBitmap()->Test(klass)) {
+ failed = true;
+ LOG(INTERNAL_FATAL) << "Unmarked object in image through live bitmap " << *space;
+ }
+ if (failed) {
+ GetThread()->Dump(LOG(INTERNAL_FATAL));
+ space->AsImageSpace()->DumpSections(LOG(INTERNAL_FATAL));
+ LOG(INTERNAL_FATAL) << "Method@" << method->GetDexMethodIndex() << ":" << method
+ << " klass@" << klass;
+ // Pretty info last in case it crashes.
+ LOG(FATAL) << "Method " << PrettyMethod(method) << " klass " << PrettyClass(klass);
+ }
+ }
+ }
mirror::Object* new_ref = klass;
visitor_(&new_ref, -1, this);
if (new_ref != klass) {
diff --git a/test/064-field-access/expected.txt b/test/064-field-access/expected.txt
index 0af56ba..69a586c 100644
--- a/test/064-field-access/expected.txt
+++ b/test/064-field-access/expected.txt
@@ -1,2 +1,3 @@
good
Got expected failure
+Got expected failure
diff --git a/test/064-field-access/smali/SubClassUsingInaccessibleField.smali b/test/064-field-access/smali/SubClassUsingInaccessibleField.smali
new file mode 100644
index 0000000..224b431
--- /dev/null
+++ b/test/064-field-access/smali/SubClassUsingInaccessibleField.smali
@@ -0,0 +1,32 @@
+# 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.
+
+.class public LSubClassUsingInaccessibleField;
+
+.super Lother/PublicClass;
+
+.method public constructor <init>()V
+ .registers 1
+ invoke-direct {p0}, Lother/PublicClass;-><init>()V
+ return-void
+.end method
+
+# Regression test for compiler DCHECK() failure (bogus check) when referencing
+# a package-private field from an indirectly inherited package-private class,
+# using this very class as the declaring class in the FieldId, bug: 27684368 .
+.method public test()I
+ .registers 2
+ iget v0, p0, LSubClassUsingInaccessibleField;->otherProtectedClassPackageIntInstanceField:I
+ return v0
+.end method
diff --git a/test/064-field-access/src/Main.java b/test/064-field-access/src/Main.java
index 8dd22ba..5d90129 100644
--- a/test/064-field-access/src/Main.java
+++ b/test/064-field-access/src/Main.java
@@ -16,6 +16,7 @@
import other.PublicClass;
import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/*
@@ -35,6 +36,20 @@
// reference
System.out.println("Got expected failure");
}
+
+ try {
+ Class c = Class.forName("SubClassUsingInaccessibleField");
+ Object o = c.newInstance();
+ c.getMethod("test").invoke(o, null);
+ } catch (InvocationTargetException ite) {
+ if (ite.getCause() instanceof IllegalAccessError) {
+ System.out.println("Got expected failure");
+ } else {
+ System.out.println("Got unexpected failure " + ite.getCause());
+ }
+ } catch (Exception e) {
+ System.out.println("Got unexpected failure " + e);
+ }
}
/*
diff --git a/test/530-checker-loops/expected.txt b/test/530-checker-loops/expected.txt
deleted file mode 100644
index e69de29..0000000
--- a/test/530-checker-loops/expected.txt
+++ /dev/null
diff --git a/test/530-checker-loops1/expected.txt b/test/530-checker-loops1/expected.txt
new file mode 100644
index 0000000..b0aad4d
--- /dev/null
+++ b/test/530-checker-loops1/expected.txt
@@ -0,0 +1 @@
+passed
diff --git a/test/530-checker-loops/info.txt b/test/530-checker-loops1/info.txt
similarity index 100%
rename from test/530-checker-loops/info.txt
rename to test/530-checker-loops1/info.txt
diff --git a/test/530-checker-loops/src/Main.java b/test/530-checker-loops1/src/Main.java
similarity index 87%
rename from test/530-checker-loops/src/Main.java
rename to test/530-checker-loops1/src/Main.java
index 2e5fd25..948a7b7 100644
--- a/test/530-checker-loops/src/Main.java
+++ b/test/530-checker-loops1/src/Main.java
@@ -454,24 +454,87 @@
return result;
}
+ /// CHECK-START: int Main.linearLong() BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: int Main.linearLong() BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK-NOT: Deoptimize
+ private static int linearLong() {
+ int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+ int result = 0;
+ // Induction on constant interval is done in higher precision than necessary,
+ // but truncated at the use as subscript.
+ for (long i = 0; i < 10; i++) {
+ result += x[(int)i];
+ }
+ return result;
+ }
+
+ /// CHECK-START: int Main.linearLongAlt(int[]) BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: int Main.linearLongAlt(int[]) BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK-NOT: Deoptimize
+ private static int linearLongAlt(int[] x) {
+ int result = 0;
+ // Induction on array length is done in higher precision than necessary,
+ // but truncated at the use as subscript.
+ for (long i = 0; i < x.length; i++) {
+ result += x[(int)i];
+ }
+ return result;
+ }
+
/// CHECK-START: int Main.linearShort() BCE (before)
/// CHECK-DAG: BoundsCheck
//
/// CHECK-START: int Main.linearShort() BCE (after)
- /// CHECK-DAG: BoundsCheck
- //
- /// CHECK-START: int Main.linearShort() BCE (after)
+ /// CHECK-NOT: BoundsCheck
/// CHECK-NOT: Deoptimize
private static int linearShort() {
int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int result = 0;
- // TODO: make this work
+ // Induction is done in short precision, but fits.
for (short i = 0; i < 10; i++) {
result += x[i];
}
return result;
}
+ /// CHECK-START: int Main.linearChar() BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: int Main.linearChar() BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK-NOT: Deoptimize
+ private static int linearChar() {
+ int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+ int result = 0;
+ // Induction is done in char precision, but fits.
+ for (char i = 0; i < 10; i++) {
+ result += x[i];
+ }
+ return result;
+ }
+
+ /// CHECK-START: int Main.linearByte() BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: int Main.linearByte() BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK-NOT: Deoptimize
+ private static int linearByte() {
+ int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+ int result = 0;
+ // Induction is done in byte precision, but fits.
+ for (byte i = 0; i < 10; i++) {
+ result += x[i];
+ }
+ return result;
+ }
+
/// CHECK-START: int Main.invariantFromPreLoop(int[], int) BCE (before)
/// CHECK-DAG: BoundsCheck
//
@@ -633,6 +696,30 @@
}
}
+ /// CHECK-START: int[] Main.linearTriangularOOB() BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: int[] Main.linearTriangularOOB() BCE (after)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: int[] Main.linearTriangularOOB() BCE (after)
+ /// CHECK-NOT: Deoptimize
+ private static int[] linearTriangularOOB() {
+ int[] a = new int[200];
+ try {
+ for (int i = 0; i < 200; i++) {
+ // Lower bound must be recognized as lower precision induction with arithmetic
+ // wrap-around to -128 when i exceeds 127.
+ for (int j = (byte) i; j < 200; j++) {
+ a[j] += 1;
+ }
+ }
+ } catch (ArrayIndexOutOfBoundsException e) {
+ return a;
+ }
+ return null; // failure if this is reached
+ }
+
//
// Verifier.
//
@@ -706,13 +793,25 @@
expectEquals(55, linearForNEArrayLengthDown(x));
expectEquals(55, linearDoWhileUp());
expectEquals(55, linearDoWhileDown());
+ expectEquals(55, linearLong());
+ expectEquals(55, linearLongAlt(x));
expectEquals(55, linearShort());
+ expectEquals(55, linearChar());
+ expectEquals(55, linearByte());
expectEquals(55, invariantFromPreLoop(x, 1));
linearTriangularOnTwoArrayLengths(10);
linearTriangularOnOneArrayLength(10);
linearTriangularOnParameter(10);
linearTriangularVariationsInnerStrict(10);
linearTriangularVariationsInnerNonStrict(10);
+ {
+ int[] t = linearTriangularOOB();
+ for (int i = 0; i < 200; i++) {
+ expectEquals(i <= 127 ? i + 1 : 128, t[i]);
+ }
+ }
+
+ System.out.println("passed");
}
private static void expectEquals(int expected, int result) {
diff --git a/test/530-checker-loops2/expected.txt b/test/530-checker-loops2/expected.txt
index e69de29..b0aad4d 100644
--- a/test/530-checker-loops2/expected.txt
+++ b/test/530-checker-loops2/expected.txt
@@ -0,0 +1 @@
+passed
diff --git a/test/530-checker-loops2/src/Main.java b/test/530-checker-loops2/src/Main.java
index 64be1a2..c644692 100644
--- a/test/530-checker-loops2/src/Main.java
+++ b/test/530-checker-loops2/src/Main.java
@@ -383,6 +383,55 @@
}
}
+ /// CHECK-START: void Main.inductionOOB(int[]) BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: void Main.inductionOOB(int[]) BCE (after)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: void Main.inductionOOB(int[]) BCE (after)
+ /// CHECK-NOT: Deoptimize
+ private static void inductionOOB(int[] a) {
+ // Careless range analysis would remove the bounds check.
+ // However, the narrower induction b wraps around arithmetically
+ // before it reaches the end of arrays longer than 127.
+ byte b = 0;
+ for (int i = 0; i < a.length; i++) {
+ a[b++] = i;
+ }
+ }
+
+ /// CHECK-START: void Main.controlOOB(int[]) BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: void Main.controlOOB(int[]) BCE (after)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: void Main.controlOOB(int[]) BCE (after)
+ /// CHECK-NOT: Deoptimize
+ private static void controlOOB(int[] a) {
+ // As above, but now the loop control also wraps around.
+ for (byte i = 0; i < a.length; i++) {
+ a[i] = -i;
+ }
+ }
+
+ /// CHECK-START: void Main.conversionOOB(int[]) BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: void Main.conversionOOB(int[]) BCE (after)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: void Main.conversionOOB(int[]) BCE (after)
+ /// CHECK-NOT: Deoptimize
+ private static void conversionOOB(int[] a) {
+ // As above, but with wrap around caused by an explicit conversion.
+ for (int i = 0; i < a.length; ) {
+ a[i] = i;
+ i = (byte) (i + 1);
+ }
+ }
+
/// CHECK-START: int[] Main.add() BCE (before)
/// CHECK-DAG: BoundsCheck
//
@@ -750,6 +799,8 @@
int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+ int[] a200 = new int[200];
+
// Sorting.
int[] sort = { 5, 4, 1, 9, 10, 2, 7, 6, 3, 8 };
bubble(sort);
@@ -884,6 +935,36 @@
sResult += 1000;
}
expectEquals(1111, sResult);
+ sResult = 0;
+ try {
+ inductionOOB(a200);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ sResult += 1000;
+ }
+ expectEquals(1000, sResult);
+ for (int i = 0; i < 200; i++) {
+ expectEquals(i < 128 ? i : 0, a200[i]);
+ }
+ sResult = 0;
+ try {
+ controlOOB(a200);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ sResult += 1000;
+ }
+ expectEquals(1000, sResult);
+ for (int i = 0; i < 200; i++) {
+ expectEquals(i < 128 ? -i : 0, a200[i]);
+ }
+ sResult = 0;
+ try {
+ conversionOOB(a200);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ sResult += 1000;
+ }
+ expectEquals(1000, sResult);
+ for (int i = 0; i < 200; i++) {
+ expectEquals(i < 128 ? i : 0, a200[i]);
+ }
// Addition.
{
@@ -989,6 +1070,8 @@
dynamicBCEAndConstantIndicesAllPrimTypes(x, x1, x2, x3, x4, x5, x6, x7, x8, 0, 10));
Integer[] x9 = { 9 };
expectEquals(145, dynamicBCEAndConstantIndexRefType(x, x9, 0, 10));
+
+ System.out.println("passed");
}
private static void expectEquals(int expected, int result) {