diff options
Diffstat (limited to 'compiler')
-rw-r--r-- | compiler/optimizing/induction_var_analysis.cc | 115 | ||||
-rw-r--r-- | compiler/optimizing/induction_var_analysis.h | 7 | ||||
-rw-r--r-- | compiler/optimizing/induction_var_analysis_test.cc | 173 | ||||
-rw-r--r-- | compiler/optimizing/induction_var_range.cc | 193 |
4 files changed, 333 insertions, 155 deletions
diff --git a/compiler/optimizing/induction_var_analysis.cc b/compiler/optimizing/induction_var_analysis.cc index b21bc09cbd..5456b1e9bf 100644 --- a/compiler/optimizing/induction_var_analysis.cc +++ b/compiler/optimizing/induction_var_analysis.cc @@ -73,10 +73,18 @@ static bool IsNarrowingIntegralConversion(Primitive::Type from, Primitive::Type } /** - * Returns narrowest data type. + * Returns result of implicit widening type conversion done in HIR. */ -static Primitive::Type Narrowest(Primitive::Type type1, Primitive::Type type2) { - return Primitive::ComponentSize(type1) <= Primitive::ComponentSize(type2) ? type1 : type2; +static Primitive::Type ImplicitConversion(Primitive::Type type) { + switch (type) { + case Primitive::kPrimShort: + case Primitive::kPrimChar: + case Primitive::kPrimByte: + case Primitive::kPrimBoolean: + return Primitive::kPrimInt; + default: + return type; + } } // @@ -232,9 +240,9 @@ void HInductionVarAnalysis::ClassifyTrivial(HLoopInformation* loop, HInstruction } else if (instruction->IsSelect()) { info = TransferPhi(loop, instruction, /*input_index*/ 0, /*adjust_input_size*/ 1); } else if (instruction->IsTypeConversion()) { - info = TransferCnv(LookupInfo(loop, instruction->InputAt(0)), - instruction->AsTypeConversion()->GetInputType(), - instruction->AsTypeConversion()->GetResultType()); + info = TransferConversion(LookupInfo(loop, instruction->InputAt(0)), + instruction->AsTypeConversion()->GetInputType(), + instruction->AsTypeConversion()->GetResultType()); } else if (instruction->IsBoundsCheck()) { info = LookupInfo(loop, instruction->InputAt(0)); // Pass-through. } @@ -267,8 +275,12 @@ void HInductionVarAnalysis::ClassifyNonTrivial(HLoopInformation* loop) { return; } - // Store interesting cycle. - AssignCycle(phi->AsPhi()); + // Store interesting cycle in each loop phi. + for (size_t i = 0; i < size; i++) { + if (scc_[i]->IsLoopHeaderPhi()) { + AssignCycle(scc_[i]->AsPhi()); + } + } // Singleton is wrap-around induction if all internal links have the same meaning. if (size == 1) { @@ -326,7 +338,7 @@ void HInductionVarAnalysis::ClassifyNonTrivial(HLoopInformation* loop) { } else if (instruction->IsSelect()) { update = SolvePhi(instruction, /*input_index*/ 0, /*adjust_input_size*/ 1); // acts like Phi } else if (instruction->IsTypeConversion()) { - update = SolveCnv(instruction->AsTypeConversion()); + update = SolveConversion(loop, phi, instruction->AsTypeConversion()); } if (update == nullptr) { return; @@ -416,8 +428,9 @@ HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferAddSub(Indu // wrap-around, or periodic can be combined with an invariant to yield a similar result. // Two linear or two polynomial inputs can be combined too. Other combinations fail. if (a != nullptr && b != nullptr) { - type_ = Narrowest(type_, Narrowest(a->type, b->type)); - if (a->induction_class == kInvariant && b->induction_class == kInvariant) { + if (IsNarrowingLinear(a) || IsNarrowingLinear(b)) { + return nullptr; // no transfer + } else if (a->induction_class == kInvariant && b->induction_class == kInvariant) { return CreateInvariantOp(op, a, b); } else if ((a->induction_class == kLinear && b->induction_class == kLinear) || (a->induction_class == kPolynomial && b->induction_class == kPolynomial)) { @@ -452,8 +465,9 @@ HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferNeg(Inducti // Transfer over a unary negation: an invariant, linear, polynomial, geometric (mul), // wrap-around, or periodic input yields a similar but negated induction as result. if (a != nullptr) { - type_ = Narrowest(type_, a->type); - if (a->induction_class == kInvariant) { + if (IsNarrowingLinear(a)) { + return nullptr; // no transfer + } else if (a->induction_class == kInvariant) { return CreateInvariantOp(kNeg, nullptr, a); } else if (a->induction_class != kGeometric || a->operation == kMul) { return CreateInduction(a->induction_class, @@ -473,8 +487,9 @@ HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferMul(Inducti // wrap-around, or periodic can be multiplied with an invariant to yield a similar // but multiplied result. Two non-invariant inputs cannot be multiplied, however. if (a != nullptr && b != nullptr) { - type_ = Narrowest(type_, Narrowest(a->type, b->type)); - if (a->induction_class == kInvariant && b->induction_class == kInvariant) { + if (IsNarrowingLinear(a) || IsNarrowingLinear(b)) { + return nullptr; // no transfer + } else if (a->induction_class == kInvariant && b->induction_class == kInvariant) { return CreateInvariantOp(kMul, a, b); } else if (a->induction_class == kInvariant && (b->induction_class != kGeometric || b->operation == kMul)) { @@ -497,17 +512,17 @@ HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferMul(Inducti return nullptr; } -HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferCnv(InductionInfo* a, - Primitive::Type from, - Primitive::Type to) { +HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferConversion( + InductionInfo* a, + Primitive::Type from, + Primitive::Type to) { if (a != nullptr) { - // Allow narrowing conversion on linear induction in certain cases. - if (IsNarrowingIntegralConversion(from, to)) { - if (a->induction_class == kLinear) { - if (a->type == to || (a->type == from && IsNarrowingIntegralConversion(from, to))) { - return CreateInduction(kLinear, kNop, a->op_a, a->op_b, /*fetch*/ nullptr, to); - } - } + // Allow narrowing conversion on linear induction in certain cases: + // induction is already at narrow type, or can be made narrower. + if (IsNarrowingIntegralConversion(from, to) && + a->induction_class == kLinear && + (a->type == to || IsNarrowingIntegralConversion(a->type, to))) { + return CreateInduction(kLinear, kNop, a->op_a, a->op_b, /*fetch*/ nullptr, to); } } return nullptr; @@ -700,16 +715,29 @@ HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::SolveTest(HLoopInfo return nullptr; } -HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::SolveCnv(HTypeConversion* conversion) { +HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::SolveConversion( + HLoopInformation* loop, + HInstruction* entry_phi, + 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; + // A narrowing conversion is allowed as *last* operation of the cycle of a linear induction + // with an initial value that fits the type, provided that the narrowest encountered type is + // recorded with the induction to account for the precision loss. The narrower induction does + // *not* transfer to any wider operations, however, since these may yield out-of-type values + if (entry_phi->InputCount() == 2 && conversion == entry_phi->InputAt(1)) { + int64_t min = Primitive::MinValueOfIntegralType(to); + int64_t max = Primitive::MaxValueOfIntegralType(to); + int64_t value = 0; + InductionInfo* initial = LookupInfo(loop, entry_phi->InputAt(0)); + if (IsNarrowingIntegralConversion(from, to) && + IsAtLeast(initial, &value) && value >= min && + IsAtMost(initial, &value) && value <= max) { + auto it = cycle_.find(conversion->GetInput()); + if (it != cycle_.end() && it->second->induction_class == kInvariant) { + type_ = to; + return it->second; + } } } return nullptr; @@ -729,7 +757,7 @@ void HInductionVarAnalysis::VisitControl(HLoopInformation* loop) { HCondition* condition = if_expr->AsCondition(); InductionInfo* a = LookupInfo(loop, condition->InputAt(0)); InductionInfo* b = LookupInfo(loop, condition->InputAt(1)); - Primitive::Type type = condition->InputAt(0)->GetType(); + Primitive::Type type = ImplicitConversion(condition->InputAt(0)->GetType()); // 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) { @@ -901,8 +929,8 @@ bool HInductionVarAnalysis::IsFinite(InductionInfo* upper_expr, int64_t stride_value, Primitive::Type type, IfCondition cmp) { - const int64_t min = Primitive::MinValueOfIntegralType(type); - const int64_t max = Primitive::MaxValueOfIntegralType(type); + int64_t min = Primitive::MinValueOfIntegralType(type); + 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) { @@ -938,8 +966,6 @@ bool HInductionVarAnalysis::FitsNarrowerControl(InductionInfo* lower_expr, min++; } // Do both bounds fit the range? - // Note: The `value` is initialized to please valgrind - the compiler can reorder - // the return value check with the `value` check, b/27651442 . int64_t value = 0; return IsAtLeast(lower_expr, &value) && value >= min && IsAtMost(lower_expr, &value) && value <= max && @@ -1046,7 +1072,8 @@ HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::CreateSimplifiedInv return CreateSimplifiedInvariant(kSub, b->op_b, b->op_a); } } - return new (graph_->GetArena()) InductionInfo(kInvariant, op, a, b, nullptr, b->type); + return new (graph_->GetArena()) InductionInfo( + kInvariant, op, a, b, nullptr, ImplicitConversion(b->type)); } HInstruction* HInductionVarAnalysis::GetShiftConstant(HLoopInformation* loop, @@ -1108,6 +1135,16 @@ bool HInductionVarAnalysis::IsAtLeast(InductionInfo* info, int64_t* value) { return InductionVarRange(this).IsConstant(info, InductionVarRange::kAtLeast, value); } +bool HInductionVarAnalysis::IsNarrowingLinear(InductionInfo* info) { + return info != nullptr && + info->induction_class == kLinear && + (info->type == Primitive::kPrimByte || + info->type == Primitive::kPrimShort || + info->type == Primitive::kPrimChar || + (info->type == Primitive::kPrimInt && (info->op_a->type == Primitive::kPrimLong || + info->op_b->type == Primitive::kPrimLong))); +} + bool HInductionVarAnalysis::InductionEqual(InductionInfo* info1, InductionInfo* info2) { // Test structural equality only, without accounting for simplifications. diff --git a/compiler/optimizing/induction_var_analysis.h b/compiler/optimizing/induction_var_analysis.h index 293aa70525..39b39cdf55 100644 --- a/compiler/optimizing/induction_var_analysis.h +++ b/compiler/optimizing/induction_var_analysis.h @@ -167,7 +167,7 @@ class HInductionVarAnalysis : public HOptimization { InductionInfo* TransferAddSub(InductionInfo* a, InductionInfo* b, InductionOp op); InductionInfo* TransferNeg(InductionInfo* a); InductionInfo* TransferMul(InductionInfo* a, InductionInfo* b); - InductionInfo* TransferCnv(InductionInfo* a, Primitive::Type from, Primitive::Type to); + InductionInfo* TransferConversion(InductionInfo* a, Primitive::Type from, Primitive::Type to); // Solvers. InductionInfo* SolvePhi(HInstruction* phi, size_t input_index, size_t adjust_input_size); @@ -191,7 +191,9 @@ class HInductionVarAnalysis : public HOptimization { HInstruction* entry_phi, HInstruction* instruction, int64_t oppositive_value); - InductionInfo* SolveCnv(HTypeConversion* conversion); + InductionInfo* SolveConversion(HLoopInformation* loop, + HInstruction* entry_phi, + HTypeConversion* conversion); // Trip count information. void VisitControl(HLoopInformation* loop); @@ -235,6 +237,7 @@ class HInductionVarAnalysis : public HOptimization { bool IsAtLeast(InductionInfo* info, /*out*/ int64_t* value); // Helpers. + static bool IsNarrowingLinear(InductionInfo* info); static bool InductionEqual(InductionInfo* info1, InductionInfo* info2); static std::string FetchToString(HInstruction* fetch); static std::string InductionToString(InductionInfo* info); diff --git a/compiler/optimizing/induction_var_analysis_test.cc b/compiler/optimizing/induction_var_analysis_test.cc index f52a1aad5a..82ee93d5c2 100644 --- a/compiler/optimizing/induction_var_analysis_test.cc +++ b/compiler/optimizing/induction_var_analysis_test.cc @@ -174,6 +174,12 @@ class InductionVarAnalysisTest : public CommonCompilerTest { iva_->LookupInfo(loop_body_[0]->GetLoopInformation(), instruction2)); } + // Returns true for narrowing linear induction. + bool IsNarrowingLinear(HInstruction* instruction) { + return HInductionVarAnalysis::IsNarrowingLinear( + iva_->LookupInfo(loop_body_[0]->GetLoopInformation(), instruction)); + } + // Performs InductionVarAnalysis (after proper set up). void PerformInductionVarAnalysis() { graph_->BuildDominatorTree(); @@ -1066,16 +1072,20 @@ TEST_F(InductionVarAnalysisTest, ByteInductionIntLoopControl) { // } BuildLoopNest(1); HInstruction* conv = InsertInstruction( - new (&allocator_) HTypeConversion(Primitive::kPrimByte, basic_[0], -1), 0); + new (&allocator_) HTypeConversion(Primitive::kPrimByte, basic_[0], kNoDexPc), 0); HInstruction* store1 = InsertArrayStore(conv, 0); HInstruction* store2 = InsertArrayStore(basic_[0], 0); PerformInductionVarAnalysis(); - // Regular int induction (i) is "transferred" over conversion into byte induction (k). + // Regular int induction (i) is transferred over conversion into byte induction (k). EXPECT_STREQ("((1) * i + (0)):PrimByte", GetInductionInfo(store1->InputAt(1), 0).c_str()); EXPECT_STREQ("((1) * i + (0)):PrimInt", GetInductionInfo(store2->InputAt(1), 0).c_str()); EXPECT_STREQ("((1) * i + (1)):PrimInt", GetInductionInfo(increment_[0], 0).c_str()); + // Narrowing detected. + EXPECT_TRUE(IsNarrowingLinear(store1->InputAt(1))); + EXPECT_FALSE(IsNarrowingLinear(store2->InputAt(1))); + // Type matters! EXPECT_FALSE(HaveSameInduction(store1->InputAt(1), store2->InputAt(1))); @@ -1093,7 +1103,7 @@ TEST_F(InductionVarAnalysisTest, ByteInductionDerivedIntLoopControl) { // } BuildLoopNest(1); HInstruction* conv = InsertInstruction( - new (&allocator_) HTypeConversion(Primitive::kPrimByte, basic_[0], -1), 0); + new (&allocator_) HTypeConversion(Primitive::kPrimByte, basic_[0], kNoDexPc), 0); HInstruction* store1 = InsertArrayStore(conv, 0); HInstruction* add = InsertInstruction( new (&allocator_) HAdd(Primitive::kPrimInt, conv, constant1_), 0); @@ -1101,11 +1111,86 @@ TEST_F(InductionVarAnalysisTest, ByteInductionDerivedIntLoopControl) { PerformInductionVarAnalysis(); - // Byte induction (k) is "transferred" over conversion into addition (k + 1). - // This means only values within byte range can be trusted (even though - // addition can jump out of the range of course). + // Byte induction (k) is detected, but it does not transfer over the addition, + // since this may yield out-of-type values. EXPECT_STREQ("((1) * i + (0)):PrimByte", GetInductionInfo(store1->InputAt(1), 0).c_str()); - EXPECT_STREQ("((1) * i + (1)):PrimByte", GetInductionInfo(store2->InputAt(1), 0).c_str()); + EXPECT_STREQ("", GetInductionInfo(store2->InputAt(1), 0).c_str()); + + // Narrowing detected. + EXPECT_TRUE(IsNarrowingLinear(store1->InputAt(1))); + EXPECT_FALSE(IsNarrowingLinear(store2->InputAt(1))); // works for null +} + +TEST_F(InductionVarAnalysisTest, ByteInduction) { + // Setup: + // k = -128; + // for (int i = 0; i < 100; i++) { + // k = k + 1; + // k = (byte) k; + // } + BuildLoopNest(1); + HPhi* k_header = InsertLoopPhi(0, 0); + k_header->AddInput(graph_->GetIntConstant(-128)); + + HInstruction* add = InsertInstruction( + new (&allocator_) HAdd(Primitive::kPrimInt, k_header, constant1_), 0); + HInstruction* conv = InsertInstruction( + new (&allocator_) HTypeConversion(Primitive::kPrimByte, add, kNoDexPc), 0); + k_header->AddInput(conv); + PerformInductionVarAnalysis(); + + // Byte induction (k) is detected, but it does not transfer over the addition, + // since this may yield out-of-type values. + EXPECT_STREQ("((1) * i + (-128)):PrimByte", GetInductionInfo(k_header, 0).c_str()); + EXPECT_STREQ("", GetInductionInfo(add, 0).c_str()); + + // Narrowing detected. + EXPECT_TRUE(IsNarrowingLinear(k_header)); + EXPECT_FALSE(IsNarrowingLinear(add)); // works for null +} + +TEST_F(InductionVarAnalysisTest, NoByteInduction1) { + // Setup: + // k = -129; / does not fit! + // for (int i = 0; i < 100; i++) { + // k = k + 1; + // k = (byte) k; + // } + BuildLoopNest(1); + HPhi* k_header = InsertLoopPhi(0, 0); + k_header->AddInput(graph_->GetIntConstant(-129)); + + HInstruction* add = InsertInstruction( + new (&allocator_) HAdd(Primitive::kPrimInt, k_header, constant1_), 0); + HInstruction* conv = InsertInstruction( + new (&allocator_) HTypeConversion(Primitive::kPrimByte, add, kNoDexPc), 0); + k_header->AddInput(conv); + PerformInductionVarAnalysis(); + + EXPECT_STREQ("", GetInductionInfo(k_header, 0).c_str()); + EXPECT_STREQ("", GetInductionInfo(add, 0).c_str()); +} + +TEST_F(InductionVarAnalysisTest, NoByteInduction2) { + // Setup: + // k = 0; + // for (int i = 0; i < 100; i++) { + // k = (byte) k; // conversion not done last! + // k = k + 1; + // } + BuildLoopNest(1); + HPhi* k_header = InsertLoopPhi(0, 0); + k_header->AddInput(constant0_); + + HInstruction* conv = InsertInstruction( + new (&allocator_) HTypeConversion(Primitive::kPrimByte, k_header, kNoDexPc), 0); + HInstruction* add = InsertInstruction( + new (&allocator_) HAdd(Primitive::kPrimInt, conv, constant1_), 0); + k_header->AddInput(add); + PerformInductionVarAnalysis(); + + EXPECT_STREQ("", GetInductionInfo(k_header, 0).c_str()); + EXPECT_STREQ("", GetInductionInfo(add, 0).c_str()); } TEST_F(InductionVarAnalysisTest, ByteLoopControl1) { @@ -1116,12 +1201,20 @@ TEST_F(InductionVarAnalysisTest, ByteLoopControl1) { 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); + HInstruction* conv = + new (&allocator_) HTypeConversion(Primitive::kPrimByte, increment_[0], kNoDexPc); 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()); + // Recorded at the phi, but not transferred to increment. + EXPECT_STREQ("((1) * i + (-128)):PrimByte", GetInductionInfo(basic_[0], 0).c_str()); + EXPECT_STREQ("", GetInductionInfo(increment_[0], 0).c_str()); + + // Narrowing detected. + EXPECT_TRUE(IsNarrowingLinear(basic_[0])); + EXPECT_FALSE(IsNarrowingLinear(increment_[0])); // works for null + // Trip-count. EXPECT_STREQ("(((127) - (-128)) (TC-loop) ((-128) < (127)))", GetTripCount(0).c_str()); } @@ -1134,12 +1227,20 @@ TEST_F(InductionVarAnalysisTest, ByteLoopControl2) { 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); + HInstruction* conv = + new (&allocator_) HTypeConversion(Primitive::kPrimByte, increment_[0], kNoDexPc); 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()); + // Recorded at the phi, but not transferred to increment. + EXPECT_STREQ("((1) * i + (-128)):PrimByte", GetInductionInfo(basic_[0], 0).c_str()); + EXPECT_STREQ("", GetInductionInfo(increment_[0], 0).c_str()); + + // Narrowing detected. + EXPECT_TRUE(IsNarrowingLinear(basic_[0])); + EXPECT_FALSE(IsNarrowingLinear(increment_[0])); // works for null + // Trip-count undefined. EXPECT_STREQ("", GetTripCount(0).c_str()); } @@ -1152,13 +1253,20 @@ TEST_F(InductionVarAnalysisTest, ShortLoopControl1) { 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); + HInstruction* conv = + new (&allocator_) HTypeConversion(Primitive::kPrimShort, increment_[0], kNoDexPc); 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()); + // Recorded at the phi, but not transferred to increment. + EXPECT_STREQ("((1) * i + (-32768)):PrimShort", GetInductionInfo(basic_[0], 0).c_str()); + EXPECT_STREQ("", GetInductionInfo(increment_[0], 0).c_str()); + + // Narrowing detected. + EXPECT_TRUE(IsNarrowingLinear(basic_[0])); + EXPECT_FALSE(IsNarrowingLinear(increment_[0])); // works for null + // Trip-count. EXPECT_STREQ("(((32767) - (-32768)) (TC-loop) ((-32768) < (32767)))", GetTripCount(0).c_str()); } @@ -1171,13 +1279,20 @@ TEST_F(InductionVarAnalysisTest, ShortLoopControl2) { 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); + HInstruction* conv = + new (&allocator_) HTypeConversion(Primitive::kPrimShort, increment_[0], kNoDexPc); 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()); + // Recorded at the phi, but not transferred to increment. + EXPECT_STREQ("((1) * i + (-32768)):PrimShort", GetInductionInfo(basic_[0], 0).c_str()); + EXPECT_STREQ("", GetInductionInfo(increment_[0], 0).c_str()); + + // Narrowing detected. + EXPECT_TRUE(IsNarrowingLinear(basic_[0])); + EXPECT_FALSE(IsNarrowingLinear(increment_[0])); // works for null + // Trip-count undefined. EXPECT_STREQ("", GetTripCount(0).c_str()); } @@ -1189,12 +1304,20 @@ TEST_F(InductionVarAnalysisTest, CharLoopControl1) { 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); + HInstruction* conv = + new (&allocator_) HTypeConversion(Primitive::kPrimChar, increment_[0], kNoDexPc); 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()); + // Recorded at the phi, but not transferred to increment. + EXPECT_STREQ("((1) * i + (0)):PrimChar", GetInductionInfo(basic_[0], 0).c_str()); + EXPECT_STREQ("", GetInductionInfo(increment_[0], 0).c_str()); + + // Narrowing detected. + EXPECT_TRUE(IsNarrowingLinear(basic_[0])); + EXPECT_FALSE(IsNarrowingLinear(increment_[0])); // works for null + // Trip-count. EXPECT_STREQ("((65535) (TC-loop) ((0) < (65535)))", GetTripCount(0).c_str()); } @@ -1206,12 +1329,20 @@ TEST_F(InductionVarAnalysisTest, CharLoopControl2) { 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); + HInstruction* conv = + new (&allocator_) HTypeConversion(Primitive::kPrimChar, increment_[0], kNoDexPc); 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()); + // Recorded at the phi, but not transferred to increment. + EXPECT_STREQ("((1) * i + (0)):PrimChar", GetInductionInfo(basic_[0], 0).c_str()); + EXPECT_STREQ("", GetInductionInfo(increment_[0], 0).c_str()); + + // Narrowing detected. + EXPECT_TRUE(IsNarrowingLinear(basic_[0])); + EXPECT_FALSE(IsNarrowingLinear(increment_[0])); // works for null + // Trip-count undefined. EXPECT_STREQ("", GetTripCount(0).c_str()); } diff --git a/compiler/optimizing/induction_var_range.cc b/compiler/optimizing/induction_var_range.cc index 7bcc3845e7..d5c4c2fa69 100644 --- a/compiler/optimizing/induction_var_range.cc +++ b/compiler/optimizing/induction_var_range.cc @@ -169,8 +169,8 @@ static InductionVarRange::Value CorrectForType(InductionVarRange::Value v, Primi 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); + int32_t min = Primitive::MinValueOfIntegralType(type); + int32_t max = Primitive::MaxValueOfIntegralType(type); return (IsConstantValue(v) && min <= v.b_constant && v.b_constant <= max) ? v : InductionVarRange::Value(); @@ -551,7 +551,7 @@ InductionVarRange::Value InductionVarRange::GetPolynomial(HInductionVarAnalysis: int64_t b = 0; if (IsConstant(info->op_a->op_a, kExact, &a) && CanLongValueFitIntoInt(a) && a >= 0 && IsConstant(info->op_a->op_b, kExact, &b) && CanLongValueFitIntoInt(b) && b >= 0) { - // Evaluate bounds on sum_i=0^m-1(a * i + b) + c with a,b >= 0 for known + // Evaluate bounds on sum_i=0^m-1(a * i + b) + c with a,b >= 0 for // maximum index value m as a * (m * (m-1)) / 2 + b * m + c. Value c = GetVal(info->op_b, trip, in_body, is_min); if (is_min) { @@ -629,6 +629,7 @@ InductionVarRange::Value InductionVarRange::GetFetch(HInstruction* instruction, } } else if (instruction->IsTypeConversion()) { // Since analysis is 32-bit (or narrower), chase beyond widening along the path. + // For example, this discovers the length in: for (long i = 0; i < a.length; i++); if (instruction->AsTypeConversion()->GetInputType() == Primitive::kPrimInt && instruction->AsTypeConversion()->GetResultType() == Primitive::kPrimLong) { return GetFetch(instruction->InputAt(0), trip, in_body, is_min); @@ -843,7 +844,7 @@ InductionVarRange::Value InductionVarRange::DivRangeAndConstant( InductionVarRange::Value InductionVarRange::AddValue(Value v1, Value v2) const { if (v1.is_known && v2.is_known && IsSafeAdd(v1.b_constant, v2.b_constant)) { - const int32_t b = v1.b_constant + v2.b_constant; + int32_t b = v1.b_constant + v2.b_constant; if (v1.a_constant == 0) { return Value(v2.instruction, v2.a_constant, b); } else if (v2.a_constant == 0) { @@ -857,7 +858,7 @@ InductionVarRange::Value InductionVarRange::AddValue(Value v1, Value v2) const { InductionVarRange::Value InductionVarRange::SubValue(Value v1, Value v2) const { if (v1.is_known && v2.is_known && IsSafeSub(v1.b_constant, v2.b_constant)) { - const int32_t b = v1.b_constant - v2.b_constant; + int32_t b = v1.b_constant - v2.b_constant; if (v1.a_constant == 0 && IsSafeSub(0, v2.a_constant)) { return Value(v2.instruction, -v2.a_constant, b); } else if (v2.a_constant == 0) { @@ -988,13 +989,16 @@ bool InductionVarRange::GenerateLastValuePolynomial(HInductionVarAnalysis::Induc IsConstant(trip->op_a, kExact, &m) && m >= 1) { // Evaluate bounds on sum_i=0^m-1(a * i + b) + c for known // maximum index value m as a * (m * (m-1)) / 2 + b * m + c. - // TODO: generalize - HInstruction* c_instr = nullptr; - if (GenerateCode(info->op_b, nullptr, graph, block, graph ? &c_instr : nullptr, false, false)) { + HInstruction* c = nullptr; + if (GenerateCode(info->op_b, nullptr, graph, block, graph ? &c : nullptr, false, false)) { if (graph != nullptr) { + Primitive::Type type = info->type; int64_t sum = a * ((m * (m - 1)) / 2) + b * m; - *result = Insert(block, new (graph->GetArena()) HAdd(info->type, - graph->GetIntConstant(sum), c_instr)); + if (type != Primitive::kPrimLong) { + sum = static_cast<int32_t>(sum); // okay to truncate + } + *result = + Insert(block, new (graph->GetArena()) HAdd(type, graph->GetConstant(type, sum), c)); } return true; } @@ -1011,35 +1015,33 @@ bool InductionVarRange::GenerateLastValueGeometric(HInductionVarAnalysis::Induct DCHECK_EQ(info->induction_class, HInductionVarAnalysis::kGeometric); // Detect known base and trip count (always taken). int64_t f = 0; - int64_t t = 0; - if (IsIntAndGet(info->fetch, &f) && f >= 1 && IsConstant(trip->op_a, kExact, &t) && t >= 1) { + int64_t m = 0; + if (IsIntAndGet(info->fetch, &f) && f >= 1 && IsConstant(trip->op_a, kExact, &m) && m >= 1) { HInstruction* opa = nullptr; HInstruction* opb = nullptr; if (GenerateCode(info->op_a, nullptr, graph, block, &opa, false, false) && GenerateCode(info->op_b, nullptr, graph, block, &opb, false, false)) { - // Compute f ^ t. - int64_t fpowt = IntPow(f, t); + // Compute f ^ m for known maximum index value m. + int64_t fpow = IntPow(f, m); if (graph != nullptr) { - DCHECK(info->type == Primitive::kPrimInt); // due to codegen, generalize? - if (fpowt == 0) { + DCHECK(info->operation == HInductionVarAnalysis::kMul || + info->operation == HInductionVarAnalysis::kDiv); + Primitive::Type type = info->type; + if (fpow == 0) { // Special case: repeated mul/div always yields zero. - *result = graph->GetIntConstant(0); - } else if (info->operation == HInductionVarAnalysis::kMul) { - // Last value multiplication: a * f ^ t + b. - HInstruction* mul = Insert(block, - new (graph->GetArena()) HMul(info->type, - opa, - graph->GetIntConstant(fpowt))); - *result = Insert(block, new (graph->GetArena()) HAdd(info->type, mul, opb)); + *result = graph->GetConstant(type, 0); } else { - // Last value multiplication: a * f ^ -t + b. - DCHECK_EQ(info->operation, HInductionVarAnalysis::kDiv); - HInstruction* div = Insert(block, - new (graph->GetArena()) HDiv(info->type, - opa, - graph->GetIntConstant(fpowt), - kNoDexPc)); - *result = Insert(block, new (graph->GetArena()) HAdd(info->type, div, opb)); + // Last value: a * f ^ m + b or a * f ^ -m + b. + if (type != Primitive::kPrimLong) { + fpow = static_cast<int32_t>(fpow); // okay to truncate + } + HInstruction* e = nullptr; + if (info->operation == HInductionVarAnalysis::kMul) { + e = new (graph->GetArena()) HMul(type, opa, graph->GetConstant(type, fpow)); + } else { + e = new (graph->GetArena()) HDiv(type, opa, graph->GetConstant(type, fpow), kNoDexPc); + } + *result = Insert(block, new (graph->GetArena()) HAdd(type, Insert(block, e), opb)); } } return true; @@ -1060,12 +1062,11 @@ bool InductionVarRange::GenerateLastValueWrapAround(HInductionVarAnalysis::Induc for (; info->induction_class == HInductionVarAnalysis::kWrapAround; info = info->op_b, ++depth) {} // Handle wrap(x, wrap(.., y)) if trip count reaches an invariant at end. - // TODO: generalize - int64_t t = 0; + // TODO: generalize, but be careful to adjust the terminal. + int64_t m = 0; if (info->induction_class == HInductionVarAnalysis::kInvariant && - IsConstant(trip->op_a, kExact, &t) && t >= depth && - GenerateCode(info, nullptr, graph, block, result, false, false)) { - return true; + IsConstant(trip->op_a, kExact, &m) && m >= depth) { + return GenerateCode(info, nullptr, graph, block, result, false, false); } return false; } @@ -1079,43 +1080,49 @@ bool InductionVarRange::GenerateLastValuePeriodic(HInductionVarAnalysis::Inducti DCHECK(info != nullptr); DCHECK_EQ(info->induction_class, HInductionVarAnalysis::kPeriodic); // Count period. - int32_t period = 1; + int64_t period = 1; for (HInductionVarAnalysis::InductionInfo* p = info; p->induction_class == HInductionVarAnalysis::kPeriodic; p = p->op_b, ++period) {} - // Handle periodic(x, y) case for restricted types. - // TODO: generalize - if (period != 2 || - trip->op_a->type != Primitive::kPrimInt || - (info->type != Primitive::kPrimInt && info->type != Primitive::kPrimBoolean)) { - return false; + // Handle any periodic(x, periodic(.., y)) for known maximum index value m. + int64_t m = 0; + if (IsConstant(trip->op_a, kExact, &m) && m >= 1) { + int64_t li = m % period; + for (int64_t i = 0; i < li; info = info->op_b, i++) {} + if (info->induction_class == HInductionVarAnalysis::kPeriodic) { + info = info->op_a; + } + return GenerateCode(info, nullptr, graph, block, result, false, false); } - HInstruction* x_instr = nullptr; - HInstruction* y_instr = nullptr; - HInstruction* trip_expr = nullptr; - if (GenerateCode(info->op_a, nullptr, graph, block, graph ? &x_instr : nullptr, false, false) && - GenerateCode(info->op_b, nullptr, graph, block, graph ? &y_instr : nullptr, false, false) && - GenerateCode(trip->op_a, nullptr, graph, block, graph ? &trip_expr : nullptr, false, false)) { - // During actual code generation (graph != nullptr), - // generate is_even ? x : y select instruction. + // Handle periodic(x, y) using even/odd-select on trip count. Enter trip count expression + // directly to obtain the maximum index value t even if taken test is needed. + HInstruction* x = nullptr; + HInstruction* y = nullptr; + HInstruction* t = nullptr; + if (period == 2 && + GenerateCode(info->op_a, nullptr, graph, block, graph ? &x : nullptr, false, false) && + GenerateCode(info->op_b, nullptr, graph, block, graph ? &y : nullptr, false, false) && + GenerateCode(trip->op_a, nullptr, graph, block, graph ? &t : nullptr, false, false)) { + // During actual code generation (graph != nullptr), generate is_even ? x : y. if (graph != nullptr) { - HInstruction* is_even = Insert(block, new (graph->GetArena()) HEqual( - Insert(block, new (graph->GetArena()) HAnd( - Primitive::kPrimInt, trip_expr, graph->GetIntConstant(1))), - graph->GetIntConstant(0), kNoDexPc)); - *result = Insert(block, new (graph->GetArena()) HSelect(is_even, x_instr, y_instr, kNoDexPc)); + Primitive::Type type = trip->type; + HInstruction* msk = + Insert(block, new (graph->GetArena()) HAnd(type, t, graph->GetConstant(type, 1))); + HInstruction* is_even = + Insert(block, new (graph->GetArena()) HEqual(msk, graph->GetConstant(type, 0), kNoDexPc)); + *result = Insert(block, new (graph->GetArena()) HSelect(is_even, x, y, kNoDexPc)); } // Guard select with taken test if needed. if (*needs_taken_test) { - HInstruction* taken_test = nullptr; - if (!GenerateCode( - trip->op_b, nullptr, graph, block, graph ? &taken_test : nullptr, false, false)) { + HInstruction* is_taken = nullptr; + if (GenerateCode(trip->op_b, nullptr, graph, block, graph ? &is_taken : nullptr, false, false)) { + if (graph != nullptr) { + *result = Insert(block, new (graph->GetArena()) HSelect(is_taken, *result, x, kNoDexPc)); + } + *needs_taken_test = false; // taken care of + } else { return false; - } else if (graph != nullptr) { - *result = Insert(block, - new (graph->GetArena()) HSelect(taken_test, *result, x_instr, kNoDexPc)); } - *needs_taken_test = false; // taken care of } return true; } @@ -1134,13 +1141,8 @@ bool InductionVarRange::GenerateCode(HInductionVarAnalysis::InductionInfo* info, if (graph != nullptr && result == nullptr) { return true; } - // Verify type safety. - // TODO: generalize - Primitive::Type type = Primitive::kPrimInt; - if (info->type != Primitive::kPrimInt && info->type != Primitive::kPrimBoolean) { - return false; - } // Handle current operation. + Primitive::Type type = info->type; HInstruction* opa = nullptr; HInstruction* opb = nullptr; switch (info->induction_class) { @@ -1214,15 +1216,15 @@ bool InductionVarRange::GenerateCode(HInductionVarAnalysis::InductionInfo* info, case HInductionVarAnalysis::kTripCountInBodyUnsafe: if (is_min) { if (graph != nullptr) { - *result = graph->GetIntConstant(0); + *result = graph->GetConstant(type, 0); } return true; } else if (in_body) { if (GenerateCode(info->op_a, trip, graph, block, &opb, in_body, is_min)) { if (graph != nullptr) { - *result = Insert(block, - new (graph->GetArena()) - HSub(type, opb, graph->GetIntConstant(1))); + *result = + Insert(block, + new (graph->GetArena()) HSub(type, opb, graph->GetConstant(type, 1))); } return true; } @@ -1236,26 +1238,31 @@ bool InductionVarRange::GenerateCode(HInductionVarAnalysis::InductionInfo* info, // Linear induction a * i + b, for normalized 0 <= i < TC. For ranges, this should // be restricted to a unit stride to avoid arithmetic wrap-around situations that // are harder to guard against. For a last value, requesting min/max based on any - // stride yields right value. - int64_t stride_value = 0; - if (IsConstant(info->op_a, kExact, &stride_value)) { - const bool is_min_a = stride_value >= 0 ? is_min : !is_min; - if (GenerateCode(trip, trip, graph, block, &opa, in_body, is_min_a) && - GenerateCode(info->op_b, trip, graph, block, &opb, in_body, is_min)) { - if (graph != nullptr) { - HInstruction* oper; - if (stride_value == 1) { - oper = new (graph->GetArena()) HAdd(type, opa, opb); - } else if (stride_value == -1) { - oper = new (graph->GetArena()) HSub(type, opb, opa); - } else { - HInstruction* mul = new (graph->GetArena()) HMul( - type, graph->GetIntConstant(stride_value), opa); - oper = new (graph->GetArena()) HAdd(type, Insert(block, mul), opb); + // known stride yields right value. Always avoid any narrowing linear induction or + // any type mismatch between the linear induction and the trip count expression. + // TODO: careful runtime type conversions could generalize this latter restriction. + if (!HInductionVarAnalysis::IsNarrowingLinear(info) && trip->type == type) { + int64_t stride_value = 0; + if (IsConstant(info->op_a, kExact, &stride_value) && + CanLongValueFitIntoInt(stride_value)) { + const bool is_min_a = stride_value >= 0 ? is_min : !is_min; + if (GenerateCode(trip, trip, graph, block, &opa, in_body, is_min_a) && + GenerateCode(info->op_b, trip, graph, block, &opb, in_body, is_min)) { + if (graph != nullptr) { + HInstruction* oper; + if (stride_value == 1) { + oper = new (graph->GetArena()) HAdd(type, opa, opb); + } else if (stride_value == -1) { + oper = new (graph->GetArena()) HSub(type, opb, opa); + } else { + HInstruction* mul = + new (graph->GetArena()) HMul(type, graph->GetConstant(type, stride_value), opa); + oper = new (graph->GetArena()) HAdd(type, Insert(block, mul), opb); + } + *result = Insert(block, oper); } - *result = Insert(block, oper); + return true; } - return true; } } break; @@ -1270,7 +1277,7 @@ bool InductionVarRange::GenerateCode(HInductionVarAnalysis::InductionInfo* info, Value extreme = GetVal(info, trip, in_body, is_min); if (IsConstantValue(extreme)) { if (graph != nullptr) { - *result = graph->GetIntConstant(extreme.b_constant); + *result = graph->GetConstant(type, extreme.b_constant); } return true; } |