You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1366 lines
49 KiB
1366 lines
49 KiB
/*
|
|
* Copyright (C) 2015 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.
|
|
*/
|
|
|
|
#include <regex>
|
|
|
|
#include "base/arena_allocator.h"
|
|
#include "builder.h"
|
|
#include "induction_var_analysis.h"
|
|
#include "nodes.h"
|
|
#include "optimizing_unit_test.h"
|
|
|
|
namespace art {
|
|
|
|
/**
|
|
* Fixture class for the InductionVarAnalysis tests.
|
|
*/
|
|
class InductionVarAnalysisTest : public OptimizingUnitTest {
|
|
public:
|
|
InductionVarAnalysisTest()
|
|
: iva_(nullptr),
|
|
entry_(nullptr),
|
|
return_(nullptr),
|
|
exit_(nullptr),
|
|
parameter_(nullptr),
|
|
constant0_(nullptr),
|
|
constant1_(nullptr),
|
|
constant2_(nullptr),
|
|
constant7_(nullptr),
|
|
constant100_(nullptr),
|
|
constantm1_(nullptr),
|
|
float_constant0_(nullptr) {
|
|
graph_ = CreateGraph();
|
|
}
|
|
|
|
~InductionVarAnalysisTest() { }
|
|
|
|
// Builds single for-loop at depth d.
|
|
void BuildForLoop(int d, int n) {
|
|
ASSERT_LT(d, n);
|
|
loop_preheader_[d] = new (GetAllocator()) HBasicBlock(graph_);
|
|
graph_->AddBlock(loop_preheader_[d]);
|
|
loop_header_[d] = new (GetAllocator()) HBasicBlock(graph_);
|
|
graph_->AddBlock(loop_header_[d]);
|
|
loop_preheader_[d]->AddSuccessor(loop_header_[d]);
|
|
if (d < (n - 1)) {
|
|
BuildForLoop(d + 1, n);
|
|
}
|
|
loop_body_[d] = new (GetAllocator()) HBasicBlock(graph_);
|
|
graph_->AddBlock(loop_body_[d]);
|
|
loop_body_[d]->AddSuccessor(loop_header_[d]);
|
|
if (d < (n - 1)) {
|
|
loop_header_[d]->AddSuccessor(loop_preheader_[d + 1]);
|
|
loop_header_[d + 1]->AddSuccessor(loop_body_[d]);
|
|
} else {
|
|
loop_header_[d]->AddSuccessor(loop_body_[d]);
|
|
}
|
|
}
|
|
|
|
// Builds a n-nested loop in CFG where each loop at depth 0 <= d < n
|
|
// is defined as "for (int i_d = 0; i_d < 100; i_d++)". Tests can further
|
|
// populate the loop with instructions to set up interesting scenarios.
|
|
void BuildLoopNest(int n) {
|
|
ASSERT_LE(n, 10);
|
|
graph_->SetNumberOfVRegs(n + 3);
|
|
|
|
// Build basic blocks with entry, nested loop, exit.
|
|
entry_ = new (GetAllocator()) HBasicBlock(graph_);
|
|
graph_->AddBlock(entry_);
|
|
BuildForLoop(0, n);
|
|
return_ = new (GetAllocator()) HBasicBlock(graph_);
|
|
graph_->AddBlock(return_);
|
|
exit_ = new (GetAllocator()) HBasicBlock(graph_);
|
|
graph_->AddBlock(exit_);
|
|
entry_->AddSuccessor(loop_preheader_[0]);
|
|
loop_header_[0]->AddSuccessor(return_);
|
|
return_->AddSuccessor(exit_);
|
|
graph_->SetEntryBlock(entry_);
|
|
graph_->SetExitBlock(exit_);
|
|
|
|
// Provide entry and exit instructions.
|
|
parameter_ = new (GetAllocator()) HParameterValue(
|
|
graph_->GetDexFile(), dex::TypeIndex(0), 0, DataType::Type::kReference, true);
|
|
entry_->AddInstruction(parameter_);
|
|
constant0_ = graph_->GetIntConstant(0);
|
|
constant1_ = graph_->GetIntConstant(1);
|
|
constant2_ = graph_->GetIntConstant(2);
|
|
constant7_ = graph_->GetIntConstant(7);
|
|
constant100_ = graph_->GetIntConstant(100);
|
|
constantm1_ = graph_->GetIntConstant(-1);
|
|
float_constant0_ = graph_->GetFloatConstant(0.0f);
|
|
return_->AddInstruction(new (GetAllocator()) HReturnVoid());
|
|
exit_->AddInstruction(new (GetAllocator()) HExit());
|
|
|
|
// Provide loop instructions.
|
|
for (int d = 0; d < n; d++) {
|
|
basic_[d] = new (GetAllocator()) HPhi(GetAllocator(), d, 0, DataType::Type::kInt32);
|
|
loop_preheader_[d]->AddInstruction(new (GetAllocator()) HGoto());
|
|
loop_header_[d]->AddPhi(basic_[d]);
|
|
HInstruction* compare = new (GetAllocator()) HLessThan(basic_[d], constant100_);
|
|
loop_header_[d]->AddInstruction(compare);
|
|
loop_header_[d]->AddInstruction(new (GetAllocator()) HIf(compare));
|
|
increment_[d] = new (GetAllocator()) HAdd(DataType::Type::kInt32, basic_[d], constant1_);
|
|
loop_body_[d]->AddInstruction(increment_[d]);
|
|
loop_body_[d]->AddInstruction(new (GetAllocator()) HGoto());
|
|
|
|
basic_[d]->AddInput(constant0_);
|
|
basic_[d]->AddInput(increment_[d]);
|
|
}
|
|
}
|
|
|
|
// Builds if-statement at depth d.
|
|
HPhi* BuildIf(int d, HBasicBlock** ifT, HBasicBlock** ifF) {
|
|
HBasicBlock* cond = new (GetAllocator()) HBasicBlock(graph_);
|
|
HBasicBlock* ifTrue = new (GetAllocator()) HBasicBlock(graph_);
|
|
HBasicBlock* ifFalse = new (GetAllocator()) HBasicBlock(graph_);
|
|
graph_->AddBlock(cond);
|
|
graph_->AddBlock(ifTrue);
|
|
graph_->AddBlock(ifFalse);
|
|
// Conditional split.
|
|
loop_header_[d]->ReplaceSuccessor(loop_body_[d], cond);
|
|
cond->AddSuccessor(ifTrue);
|
|
cond->AddSuccessor(ifFalse);
|
|
ifTrue->AddSuccessor(loop_body_[d]);
|
|
ifFalse->AddSuccessor(loop_body_[d]);
|
|
cond->AddInstruction(new (GetAllocator()) HIf(parameter_));
|
|
*ifT = ifTrue;
|
|
*ifF = ifFalse;
|
|
|
|
HPhi* select_phi = new (GetAllocator()) HPhi(GetAllocator(), -1, 0, DataType::Type::kInt32);
|
|
loop_body_[d]->AddPhi(select_phi);
|
|
return select_phi;
|
|
}
|
|
|
|
// Inserts instruction right before increment at depth d.
|
|
HInstruction* InsertInstruction(HInstruction* instruction, int d) {
|
|
loop_body_[d]->InsertInstructionBefore(instruction, increment_[d]);
|
|
return instruction;
|
|
}
|
|
|
|
// Inserts a phi to loop header at depth d and returns it.
|
|
HPhi* InsertLoopPhi(int vreg, int d) {
|
|
HPhi* phi = new (GetAllocator()) HPhi(GetAllocator(), vreg, 0, DataType::Type::kInt32);
|
|
loop_header_[d]->AddPhi(phi);
|
|
return phi;
|
|
}
|
|
|
|
// Inserts an array store with given `subscript` at depth d to
|
|
// enable tests to inspect the computed induction at that point easily.
|
|
HInstruction* InsertArrayStore(HInstruction* subscript, int d) {
|
|
// ArraySet is given a float value in order to avoid SsaBuilder typing
|
|
// it from the array's non-existent reference type info.
|
|
return InsertInstruction(new (GetAllocator()) HArraySet(
|
|
parameter_, subscript, float_constant0_, DataType::Type::kFloat32, 0), d);
|
|
}
|
|
|
|
// Returns induction information of instruction in loop at depth d.
|
|
std::string GetInductionInfo(HInstruction* instruction, int d) {
|
|
return HInductionVarAnalysis::InductionToString(
|
|
iva_->LookupInfo(loop_body_[d]->GetLoopInformation(), instruction));
|
|
}
|
|
|
|
// Returns induction information of the trip-count of loop at depth d.
|
|
std::string GetTripCount(int d) {
|
|
HInstruction* control = loop_header_[d]->GetLastInstruction();
|
|
DCHECK(control->IsIf());
|
|
return GetInductionInfo(control, d);
|
|
}
|
|
|
|
// Returns true if instructions have identical induction.
|
|
bool HaveSameInduction(HInstruction* instruction1, HInstruction* instruction2) {
|
|
return HInductionVarAnalysis::InductionEqual(
|
|
iva_->LookupInfo(loop_body_[0]->GetLoopInformation(), instruction1),
|
|
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();
|
|
iva_ = new (GetAllocator()) HInductionVarAnalysis(graph_);
|
|
iva_->Run();
|
|
}
|
|
|
|
// General building fields.
|
|
HGraph* graph_;
|
|
HInductionVarAnalysis* iva_;
|
|
|
|
// Fixed basic blocks and instructions.
|
|
HBasicBlock* entry_;
|
|
HBasicBlock* return_;
|
|
HBasicBlock* exit_;
|
|
HInstruction* parameter_; // "this"
|
|
HInstruction* constant0_;
|
|
HInstruction* constant1_;
|
|
HInstruction* constant2_;
|
|
HInstruction* constant7_;
|
|
HInstruction* constant100_;
|
|
HInstruction* constantm1_;
|
|
HInstruction* float_constant0_;
|
|
|
|
// Loop specifics.
|
|
HBasicBlock* loop_preheader_[10];
|
|
HBasicBlock* loop_header_[10];
|
|
HBasicBlock* loop_body_[10];
|
|
HInstruction* increment_[10];
|
|
HPhi* basic_[10]; // "vreg_d", the "i_d"
|
|
};
|
|
|
|
//
|
|
// The actual InductionVarAnalysis tests.
|
|
//
|
|
|
|
TEST_F(InductionVarAnalysisTest, ProperLoopSetup) {
|
|
// Setup:
|
|
// for (int i_0 = 0; i_0 < 100; i_0++) {
|
|
// ..
|
|
// for (int i_9 = 0; i_9 < 100; i_9++) {
|
|
// }
|
|
// ..
|
|
// }
|
|
BuildLoopNest(10);
|
|
graph_->BuildDominatorTree();
|
|
|
|
ASSERT_EQ(entry_->GetLoopInformation(), nullptr);
|
|
for (int d = 0; d < 1; d++) {
|
|
ASSERT_EQ(loop_preheader_[d]->GetLoopInformation(),
|
|
(d == 0) ? nullptr
|
|
: loop_header_[d - 1]->GetLoopInformation());
|
|
ASSERT_NE(loop_header_[d]->GetLoopInformation(), nullptr);
|
|
ASSERT_NE(loop_body_[d]->GetLoopInformation(), nullptr);
|
|
ASSERT_EQ(loop_header_[d]->GetLoopInformation(),
|
|
loop_body_[d]->GetLoopInformation());
|
|
}
|
|
ASSERT_EQ(exit_->GetLoopInformation(), nullptr);
|
|
}
|
|
|
|
TEST_F(InductionVarAnalysisTest, FindBasicInduction) {
|
|
// Setup:
|
|
// for (int i = 0; i < 100; i++) {
|
|
// a[i] = 0;
|
|
// }
|
|
BuildLoopNest(1);
|
|
HInstruction* store = InsertArrayStore(basic_[0], 0);
|
|
PerformInductionVarAnalysis();
|
|
|
|
EXPECT_STREQ("((1) * i + (0)):Int32", GetInductionInfo(store->InputAt(1), 0).c_str());
|
|
EXPECT_STREQ("((1) * i + (1)):Int32", GetInductionInfo(increment_[0], 0).c_str());
|
|
|
|
// Offset matters!
|
|
EXPECT_FALSE(HaveSameInduction(store->InputAt(1), increment_[0]));
|
|
|
|
// Trip-count.
|
|
EXPECT_STREQ("((100) (TC-loop) ((0) < (100)))", GetTripCount(0).c_str());
|
|
}
|
|
|
|
TEST_F(InductionVarAnalysisTest, FindDerivedInduction) {
|
|
// Setup:
|
|
// for (int i = 0; i < 100; i++) {
|
|
// t = 100 + i;
|
|
// t = 100 - i;
|
|
// t = 100 * i;
|
|
// t = i << 1;
|
|
// t = - i;
|
|
// }
|
|
BuildLoopNest(1);
|
|
HInstruction* add = InsertInstruction(
|
|
new (GetAllocator()) HAdd(DataType::Type::kInt32, constant100_, basic_[0]), 0);
|
|
HInstruction* sub = InsertInstruction(
|
|
new (GetAllocator()) HSub(DataType::Type::kInt32, constant100_, basic_[0]), 0);
|
|
HInstruction* mul = InsertInstruction(
|
|
new (GetAllocator()) HMul(DataType::Type::kInt32, constant100_, basic_[0]), 0);
|
|
HInstruction* shl = InsertInstruction(
|
|
new (GetAllocator()) HShl(DataType::Type::kInt32, basic_[0], constant1_), 0);
|
|
HInstruction* neg = InsertInstruction(
|
|
new (GetAllocator()) HNeg(DataType::Type::kInt32, basic_[0]), 0);
|
|
PerformInductionVarAnalysis();
|
|
|
|
EXPECT_STREQ("((1) * i + (100)):Int32", GetInductionInfo(add, 0).c_str());
|
|
EXPECT_STREQ("(( - (1)) * i + (100)):Int32", GetInductionInfo(sub, 0).c_str());
|
|
EXPECT_STREQ("((100) * i + (0)):Int32", GetInductionInfo(mul, 0).c_str());
|
|
EXPECT_STREQ("((2) * i + (0)):Int32", GetInductionInfo(shl, 0).c_str());
|
|
EXPECT_STREQ("(( - (1)) * i + (0)):Int32", GetInductionInfo(neg, 0).c_str());
|
|
}
|
|
|
|
TEST_F(InductionVarAnalysisTest, FindChainInduction) {
|
|
// Setup:
|
|
// k = 0;
|
|
// for (int i = 0; i < 100; i++) {
|
|
// k = k + 100;
|
|
// a[k] = 0;
|
|
// k = k - 1;
|
|
// a[k] = 0;
|
|
// }
|
|
BuildLoopNest(1);
|
|
HPhi* k_header = InsertLoopPhi(0, 0);
|
|
k_header->AddInput(constant0_);
|
|
|
|
HInstruction* add = InsertInstruction(
|
|
new (GetAllocator()) HAdd(DataType::Type::kInt32, k_header, constant100_), 0);
|
|
HInstruction* store1 = InsertArrayStore(add, 0);
|
|
HInstruction* sub = InsertInstruction(
|
|
new (GetAllocator()) HSub(DataType::Type::kInt32, add, constant1_), 0);
|
|
HInstruction* store2 = InsertArrayStore(sub, 0);
|
|
k_header->AddInput(sub);
|
|
PerformInductionVarAnalysis();
|
|
|
|
EXPECT_STREQ("(((100) - (1)) * i + (0)):Int32",
|
|
GetInductionInfo(k_header, 0).c_str());
|
|
EXPECT_STREQ("(((100) - (1)) * i + (100)):Int32",
|
|
GetInductionInfo(store1->InputAt(1), 0).c_str());
|
|
EXPECT_STREQ("(((100) - (1)) * i + ((100) - (1))):Int32",
|
|
GetInductionInfo(store2->InputAt(1), 0).c_str());
|
|
}
|
|
|
|
TEST_F(InductionVarAnalysisTest, FindTwoWayBasicInduction) {
|
|
// Setup:
|
|
// k = 0;
|
|
// for (int i = 0; i < 100; i++) {
|
|
// if () k = k + 1;
|
|
// else k = k + 1;
|
|
// a[k] = 0;
|
|
// }
|
|
BuildLoopNest(1);
|
|
HPhi* k_header = InsertLoopPhi(0, 0);
|
|
k_header->AddInput(constant0_);
|
|
|
|
HBasicBlock* ifTrue;
|
|
HBasicBlock* ifFalse;
|
|
HPhi* k_body = BuildIf(0, &ifTrue, &ifFalse);
|
|
|
|
// True-branch.
|
|
HInstruction* inc1 = new (GetAllocator()) HAdd(DataType::Type::kInt32, k_header, constant1_);
|
|
ifTrue->AddInstruction(inc1);
|
|
k_body->AddInput(inc1);
|
|
// False-branch.
|
|
HInstruction* inc2 = new (GetAllocator()) HAdd(DataType::Type::kInt32, k_header, constant1_);
|
|
ifFalse->AddInstruction(inc2);
|
|
k_body->AddInput(inc2);
|
|
// Merge over a phi.
|
|
HInstruction* store = InsertArrayStore(k_body, 0);
|
|
k_header->AddInput(k_body);
|
|
PerformInductionVarAnalysis();
|
|
|
|
EXPECT_STREQ("((1) * i + (0)):Int32", GetInductionInfo(k_header, 0).c_str());
|
|
EXPECT_STREQ("((1) * i + (1)):Int32", GetInductionInfo(store->InputAt(1), 0).c_str());
|
|
|
|
// Both increments get same induction.
|
|
EXPECT_TRUE(HaveSameInduction(store->InputAt(1), inc1));
|
|
EXPECT_TRUE(HaveSameInduction(store->InputAt(1), inc2));
|
|
}
|
|
|
|
TEST_F(InductionVarAnalysisTest, FindTwoWayDerivedInduction) {
|
|
// Setup:
|
|
// for (int i = 0; i < 100; i++) {
|
|
// if () k = i + 1;
|
|
// else k = i + 1;
|
|
// a[k] = 0;
|
|
// }
|
|
BuildLoopNest(1);
|
|
HBasicBlock* ifTrue;
|
|
HBasicBlock* ifFalse;
|
|
HPhi* k = BuildIf(0, &ifTrue, &ifFalse);
|
|
|
|
// True-branch.
|
|
HInstruction* inc1 = new (GetAllocator()) HAdd(DataType::Type::kInt32, basic_[0], constant1_);
|
|
ifTrue->AddInstruction(inc1);
|
|
k->AddInput(inc1);
|
|
// False-branch.
|
|
HInstruction* inc2 = new (GetAllocator()) HAdd(DataType::Type::kInt32, basic_[0], constant1_);
|
|
ifFalse->AddInstruction(inc2);
|
|
k->AddInput(inc2);
|
|
// Merge over a phi.
|
|
HInstruction* store = InsertArrayStore(k, 0);
|
|
PerformInductionVarAnalysis();
|
|
|
|
EXPECT_STREQ("((1) * i + (1)):Int32", GetInductionInfo(store->InputAt(1), 0).c_str());
|
|
|
|
// Both increments get same induction.
|
|
EXPECT_TRUE(HaveSameInduction(store->InputAt(1), inc1));
|
|
EXPECT_TRUE(HaveSameInduction(store->InputAt(1), inc2));
|
|
}
|
|
|
|
TEST_F(InductionVarAnalysisTest, AddLinear) {
|
|
// Setup:
|
|
// for (int i = 0; i < 100; i++) {
|
|
// t1 = i + i;
|
|
// t2 = 7 + i;
|
|
// t3 = t1 + t2;
|
|
// }
|
|
BuildLoopNest(1);
|
|
|
|
HInstruction* add1 = InsertInstruction(
|
|
new (GetAllocator()) HAdd(DataType::Type::kInt32, basic_[0], basic_[0]), 0);
|
|
HInstruction* add2 = InsertInstruction(
|
|
new (GetAllocator()) HAdd(DataType::Type::kInt32, constant7_, basic_[0]), 0);
|
|
HInstruction* add3 = InsertInstruction(
|
|
new (GetAllocator()) HAdd(DataType::Type::kInt32, add1, add2), 0);
|
|
PerformInductionVarAnalysis();
|
|
|
|
EXPECT_STREQ("((1) * i + (0)):Int32", GetInductionInfo(basic_[0], 0).c_str());
|
|
EXPECT_STREQ("(((1) + (1)) * i + (0)):Int32", GetInductionInfo(add1, 0).c_str());
|
|
EXPECT_STREQ("((1) * i + (7)):Int32", GetInductionInfo(add2, 0).c_str());
|
|
EXPECT_STREQ("((((1) + (1)) + (1)) * i + (7)):Int32", GetInductionInfo(add3, 0).c_str());
|
|
}
|
|
|
|
TEST_F(InductionVarAnalysisTest, FindPolynomialInduction) {
|
|
// Setup:
|
|
// k = 1;
|
|
// for (int i = 0; i < 100; i++) {
|
|
// t = i * 2;
|
|
// t = 100 + t
|
|
// k = t + k; // polynomial
|
|
// }
|
|
BuildLoopNest(1);
|
|
HPhi* k_header = InsertLoopPhi(0, 0);
|
|
k_header->AddInput(constant1_);
|
|
|
|
HInstruction* mul = InsertInstruction(
|
|
new (GetAllocator()) HMul(DataType::Type::kInt32, basic_[0], constant2_), 0);
|
|
HInstruction* add = InsertInstruction(
|
|
new (GetAllocator()) HAdd(DataType::Type::kInt32, constant100_, mul), 0);
|
|
HInstruction* pol = InsertInstruction(
|
|
new (GetAllocator()) HAdd(DataType::Type::kInt32, add, k_header), 0);
|
|
k_header->AddInput(pol);
|
|
PerformInductionVarAnalysis();
|
|
|
|
// Note, only the phi in the cycle and the base linear induction are classified.
|
|
EXPECT_STREQ("poly(sum_lt(((2) * i + (100)):Int32) + (1)):Int32",
|
|
GetInductionInfo(k_header, 0).c_str());
|
|
EXPECT_STREQ("((2) * i + (100)):Int32", GetInductionInfo(add, 0).c_str());
|
|
EXPECT_STREQ("", GetInductionInfo(pol, 0).c_str());
|
|
}
|
|
|
|
TEST_F(InductionVarAnalysisTest, FindPolynomialInductionAndDerived) {
|
|
// Setup:
|
|
// k = 1;
|
|
// for (int i = 0; i < 100; i++) {
|
|
// t = k + 100;
|
|
// t = k - 1;
|
|
// t = - t
|
|
// t = k * 2;
|
|
// t = k << 2;
|
|
// k = k + i; // polynomial
|
|
// }
|
|
BuildLoopNest(1);
|
|
HPhi* k_header = InsertLoopPhi(0, 0);
|
|
k_header->AddInput(constant1_);
|
|
|
|
HInstruction* add = InsertInstruction(
|
|
new (GetAllocator()) HAdd(DataType::Type::kInt32, k_header, constant100_), 0);
|
|
HInstruction* sub = InsertInstruction(
|
|
new (GetAllocator()) HSub(DataType::Type::kInt32, k_header, constant1_), 0);
|
|
HInstruction* neg = InsertInstruction(
|
|
new (GetAllocator()) HNeg(DataType::Type::kInt32, sub), 0);
|
|
HInstruction* mul = InsertInstruction(
|
|
new (GetAllocator()) HMul(DataType::Type::kInt32, k_header, constant2_), 0);
|
|
HInstruction* shl = InsertInstruction(
|
|
new (GetAllocator()) HShl(DataType::Type::kInt32, k_header, constant2_), 0);
|
|
HInstruction* pol = InsertInstruction(
|
|
new (GetAllocator()) HAdd(DataType::Type::kInt32, k_header, basic_[0]), 0);
|
|
k_header->AddInput(pol);
|
|
PerformInductionVarAnalysis();
|
|
|
|
// Note, only the phi in the cycle and derived are classified.
|
|
EXPECT_STREQ("poly(sum_lt(((1) * i + (0)):Int32) + (1)):Int32",
|
|
GetInductionInfo(k_header, 0).c_str());
|
|
EXPECT_STREQ("poly(sum_lt(((1) * i + (0)):Int32) + ((1) + (100))):Int32",
|
|
GetInductionInfo(add, 0).c_str());
|
|
EXPECT_STREQ("poly(sum_lt(((1) * i + (0)):Int32) + ((1) - (1))):Int32",
|
|
GetInductionInfo(sub, 0).c_str());
|
|
EXPECT_STREQ("poly(sum_lt((( - (1)) * i + (0)):Int32) + ((1) - (1))):Int32",
|
|
GetInductionInfo(neg, 0).c_str());
|
|
EXPECT_STREQ("poly(sum_lt(((2) * i + (0)):Int32) + (2)):Int32",
|
|
GetInductionInfo(mul, 0).c_str());
|
|
EXPECT_STREQ("poly(sum_lt(((4) * i + (0)):Int32) + (4)):Int32",
|
|
GetInductionInfo(shl, 0).c_str());
|
|
EXPECT_STREQ("", GetInductionInfo(pol, 0).c_str());
|
|
}
|
|
|
|
TEST_F(InductionVarAnalysisTest, AddPolynomial) {
|
|
// Setup:
|
|
// k = 7;
|
|
// for (int i = 0; i < 100; i++) {
|
|
// t = k + k;
|
|
// t = t + k;
|
|
// k = k + i
|
|
// }
|
|
BuildLoopNest(1);
|
|
HPhi* k_header = InsertLoopPhi(0, 0);
|
|
k_header->AddInput(constant7_);
|
|
|
|
HInstruction* add1 = InsertInstruction(
|
|
new (GetAllocator()) HAdd(DataType::Type::kInt32, k_header, k_header), 0);
|
|
HInstruction* add2 = InsertInstruction(
|
|
new (GetAllocator()) HAdd(DataType::Type::kInt32, add1, k_header), 0);
|
|
HInstruction* add3 = InsertInstruction(
|
|
new (GetAllocator()) HAdd(DataType::Type::kInt32, k_header, basic_[0]), 0);
|
|
k_header->AddInput(add3);
|
|
PerformInductionVarAnalysis();
|
|
|
|
// Note, only the phi in the cycle and added-derived are classified.
|
|
EXPECT_STREQ("poly(sum_lt(((1) * i + (0)):Int32) + (7)):Int32",
|
|
GetInductionInfo(k_header, 0).c_str());
|
|
EXPECT_STREQ("poly(sum_lt((((1) + (1)) * i + (0)):Int32) + ((7) + (7))):Int32",
|
|
GetInductionInfo(add1, 0).c_str());
|
|
EXPECT_STREQ(
|
|
"poly(sum_lt(((((1) + (1)) + (1)) * i + (0)):Int32) + (((7) + (7)) + (7))):Int32",
|
|
GetInductionInfo(add2, 0).c_str());
|
|
EXPECT_STREQ("", GetInductionInfo(add3, 0).c_str());
|
|
}
|
|
|
|
TEST_F(InductionVarAnalysisTest, FindGeometricMulInduction) {
|
|
// Setup:
|
|
// k = 1;
|
|
// for (int i = 0; i < 100; i++) {
|
|
// k = k * 100; // geometric (x 100)
|
|
// }
|
|
BuildLoopNest(1);
|
|
HPhi* k_header = InsertLoopPhi(0, 0);
|
|
k_header->AddInput(constant1_);
|
|
|
|
HInstruction* mul = InsertInstruction(
|
|
new (GetAllocator()) HMul(DataType::Type::kInt32, k_header, constant100_), 0);
|
|
k_header->AddInput(mul);
|
|
PerformInductionVarAnalysis();
|
|
|
|
EXPECT_STREQ("geo((1) * 100 ^ i + (0)):Int32", GetInductionInfo(k_header, 0).c_str());
|
|
EXPECT_STREQ("geo((100) * 100 ^ i + (0)):Int32", GetInductionInfo(mul, 0).c_str());
|
|
}
|
|
|
|
TEST_F(InductionVarAnalysisTest, FindGeometricShlInductionAndDerived) {
|
|
// Setup:
|
|
// k = 1;
|
|
// for (int i = 0; i < 100; i++) {
|
|
// t = k + 1;
|
|
// k = k << 1; // geometric (x 2)
|
|
// t = k + 100;
|
|
// t = k - 1;
|
|
// t = - t;
|
|
// t = k * 2;
|
|
// t = k << 2;
|
|
// }
|
|
BuildLoopNest(1);
|
|
HPhi* k_header = InsertLoopPhi(0, 0);
|
|
k_header->AddInput(constant1_);
|
|
|
|
HInstruction* add1 = InsertInstruction(
|
|
new (GetAllocator()) HAdd(DataType::Type::kInt32, k_header, constant1_), 0);
|
|
HInstruction* shl1 = InsertInstruction(
|
|
new (GetAllocator()) HShl(DataType::Type::kInt32, k_header, constant1_), 0);
|
|
HInstruction* add2 = InsertInstruction(
|
|
new (GetAllocator()) HAdd(DataType::Type::kInt32, shl1, constant100_), 0);
|
|
HInstruction* sub = InsertInstruction(
|
|
new (GetAllocator()) HSub(DataType::Type::kInt32, shl1, constant1_), 0);
|
|
HInstruction* neg = InsertInstruction(
|
|
new (GetAllocator()) HNeg(DataType::Type::kInt32, sub), 0);
|
|
HInstruction* mul = InsertInstruction(
|
|
new (GetAllocator()) HMul(DataType::Type::kInt32, shl1, constant2_), 0);
|
|
HInstruction* shl2 = InsertInstruction(
|
|
new (GetAllocator()) HShl(DataType::Type::kInt32, shl1, constant2_), 0);
|
|
k_header->AddInput(shl1);
|
|
PerformInductionVarAnalysis();
|
|
|
|
EXPECT_STREQ("geo((1) * 2 ^ i + (0)):Int32", GetInductionInfo(k_header, 0).c_str());
|
|
EXPECT_STREQ("geo((1) * 2 ^ i + (1)):Int32", GetInductionInfo(add1, 0).c_str());
|
|
EXPECT_STREQ("geo((2) * 2 ^ i + (0)):Int32", GetInductionInfo(shl1, 0).c_str());
|
|
EXPECT_STREQ("geo((2) * 2 ^ i + (100)):Int32", GetInductionInfo(add2, 0).c_str());
|
|
EXPECT_STREQ("geo((2) * 2 ^ i + ((0) - (1))):Int32", GetInductionInfo(sub, 0).c_str());
|
|
EXPECT_STREQ("geo(( - (2)) * 2 ^ i + ( - ((0) - (1)))):Int32",
|
|
GetInductionInfo(neg, 0).c_str());
|
|
EXPECT_STREQ("geo(((2) * (2)) * 2 ^ i + (0)):Int32", GetInductionInfo(mul, 0).c_str());
|
|
EXPECT_STREQ("geo(((2) * (4)) * 2 ^ i + (0)):Int32", GetInductionInfo(shl2, 0).c_str());
|
|
}
|
|
|
|
TEST_F(InductionVarAnalysisTest, FindGeometricDivInductionAndDerived) {
|
|
// Setup:
|
|
// k = 1;
|
|
// for (int i = 0; i < 100; i++) {
|
|
// t = k + 100;
|
|
// t = k - 1;
|
|
// t = - t
|
|
// t = k * 2;
|
|
// t = k << 2;
|
|
// k = k / 100; // geometric (/ 100)
|
|
// }
|
|
BuildLoopNest(1);
|
|
HPhi* k_header = InsertLoopPhi(0, 0);
|
|
k_header->AddInput(constant1_);
|
|
|
|
HInstruction* add = InsertInstruction(
|
|
new (GetAllocator()) HAdd(DataType::Type::kInt32, k_header, constant100_), 0);
|
|
HInstruction* sub = InsertInstruction(
|
|
new (GetAllocator()) HSub(DataType::Type::kInt32, k_header, constant1_), 0);
|
|
HInstruction* neg = InsertInstruction(
|
|
new (GetAllocator()) HNeg(DataType::Type::kInt32, sub), 0);
|
|
HInstruction* mul = InsertInstruction(
|
|
new (GetAllocator()) HMul(DataType::Type::kInt32, k_header, constant2_), 0);
|
|
HInstruction* shl = InsertInstruction(
|
|
new (GetAllocator()) HShl(DataType::Type::kInt32, k_header, constant2_), 0);
|
|
HInstruction* div = InsertInstruction(
|
|
new (GetAllocator()) HDiv(DataType::Type::kInt32, k_header, constant100_, kNoDexPc), 0);
|
|
k_header->AddInput(div);
|
|
PerformInductionVarAnalysis();
|
|
|
|
// Note, only the phi in the cycle and direct additive derived are classified.
|
|
EXPECT_STREQ("geo((1) * 100 ^ -i + (0)):Int32", GetInductionInfo(k_header, 0).c_str());
|
|
EXPECT_STREQ("geo((1) * 100 ^ -i + (100)):Int32", GetInductionInfo(add, 0).c_str());
|
|
EXPECT_STREQ("geo((1) * 100 ^ -i + ((0) - (1))):Int32", GetInductionInfo(sub, 0).c_str());
|
|
EXPECT_STREQ("", GetInductionInfo(neg, 0).c_str());
|
|
EXPECT_STREQ("", GetInductionInfo(mul, 0).c_str());
|
|
EXPECT_STREQ("", GetInductionInfo(shl, 0).c_str());
|
|
EXPECT_STREQ("", GetInductionInfo(div, 0).c_str());
|
|
}
|
|
|
|
TEST_F(InductionVarAnalysisTest, FindGeometricShrInduction) {
|
|
// Setup:
|
|
// k = 100;
|
|
// for (int i = 0; i < 100; i++) {
|
|
// k = k >> 1; // geometric (/ 2)
|
|
// }
|
|
BuildLoopNest(1);
|
|
HPhi* k_header = InsertLoopPhi(0, 0);
|
|
k_header->AddInput(constant100_);
|
|
|
|
HInstruction* shr = InsertInstruction(
|
|
new (GetAllocator()) HShr(DataType::Type::kInt32, k_header, constant1_), 0);
|
|
k_header->AddInput(shr);
|
|
PerformInductionVarAnalysis();
|
|
|
|
// Note, only the phi in the cycle is classified.
|
|
EXPECT_STREQ("geo((100) * 2 ^ -i + (0)):Int32", GetInductionInfo(k_header, 0).c_str());
|
|
EXPECT_STREQ("", GetInductionInfo(shr, 0).c_str());
|
|
}
|
|
|
|
TEST_F(InductionVarAnalysisTest, FindNotGeometricShrInduction) {
|
|
// Setup:
|
|
// k = -1;
|
|
// for (int i = 0; i < 100; i++) {
|
|
// k = k >> 1; // initial value is negative
|
|
// }
|
|
BuildLoopNest(1);
|
|
HPhi* k_header = InsertLoopPhi(0, 0);
|
|
k_header->AddInput(constantm1_);
|
|
|
|
HInstruction* shr = InsertInstruction(
|
|
new (GetAllocator()) HShr(DataType::Type::kInt32, k_header, constant1_), 0);
|
|
k_header->AddInput(shr);
|
|
PerformInductionVarAnalysis();
|
|
|
|
EXPECT_STREQ("", GetInductionInfo(k_header, 0).c_str());
|
|
EXPECT_STREQ("", GetInductionInfo(shr, 0).c_str());
|
|
}
|
|
|
|
TEST_F(InductionVarAnalysisTest, FindRemWrapAroundInductionAndDerived) {
|
|
// Setup:
|
|
// k = 100;
|
|
// for (int i = 0; i < 100; i++) {
|
|
// t = k + 100;
|
|
// t = k - 1;
|
|
// t = -t
|
|
// t = k * 2;
|
|
// t = k << 2;
|
|
// k = k % 7;
|
|
// }
|
|
BuildLoopNest(1);
|
|
HPhi* k_header = InsertLoopPhi(0, 0);
|
|
k_header->AddInput(constant100_);
|
|
|
|
HInstruction* add = InsertInstruction(
|
|
new (GetAllocator()) HAdd(DataType::Type::kInt32, k_header, constant100_), 0);
|
|
HInstruction* sub = InsertInstruction(
|
|
new (GetAllocator()) HSub(DataType::Type::kInt32, k_header, constant1_), 0);
|
|
HInstruction* neg = InsertInstruction(
|
|
new (GetAllocator()) HNeg(DataType::Type::kInt32, sub), 0);
|
|
HInstruction* mul = InsertInstruction(
|
|
new (GetAllocator()) HMul(DataType::Type::kInt32, k_header, constant2_), 0);
|
|
HInstruction* shl = InsertInstruction(
|
|
new (GetAllocator()) HShl(DataType::Type::kInt32, k_header, constant2_), 0);
|
|
HInstruction* rem = InsertInstruction(
|
|
new (GetAllocator()) HRem(DataType::Type::kInt32, k_header, constant7_, kNoDexPc), 0);
|
|
k_header->AddInput(rem);
|
|
PerformInductionVarAnalysis();
|
|
|
|
// Note, only the phi in the cycle and derived are classified.
|
|
EXPECT_STREQ("wrap((100), ((100) % (7))):Int32", GetInductionInfo(k_header, 0).c_str());
|
|
EXPECT_STREQ("wrap(((100) + (100)), (((100) % (7)) + (100))):Int32",
|
|
GetInductionInfo(add, 0).c_str());
|
|
EXPECT_STREQ("wrap(((100) - (1)), (((100) % (7)) - (1))):Int32",
|
|
GetInductionInfo(sub, 0).c_str());
|
|
EXPECT_STREQ("wrap(( - ((100) - (1))), ( - (((100) % (7)) - (1)))):Int32",
|
|
GetInductionInfo(neg, 0).c_str());
|
|
EXPECT_STREQ("wrap(((100) * (2)), (((100) % (7)) * (2))):Int32",
|
|
GetInductionInfo(mul, 0).c_str());
|
|
EXPECT_STREQ("wrap(((100) * (4)), (((100) % (7)) * (4))):Int32",
|
|
GetInductionInfo(shl, 0).c_str());
|
|
EXPECT_STREQ("", GetInductionInfo(rem, 0).c_str());
|
|
}
|
|
|
|
TEST_F(InductionVarAnalysisTest, FindFirstOrderWrapAroundInduction) {
|
|
// Setup:
|
|
// k = 0;
|
|
// for (int i = 0; i < 100; i++) {
|
|
// a[k] = 0;
|
|
// k = 100 - i;
|
|
// }
|
|
BuildLoopNest(1);
|
|
HPhi* k_header = InsertLoopPhi(0, 0);
|
|
k_header->AddInput(constant0_);
|
|
|
|
HInstruction* store = InsertArrayStore(k_header, 0);
|
|
HInstruction* sub = InsertInstruction(
|
|
new (GetAllocator()) HSub(DataType::Type::kInt32, constant100_, basic_[0]), 0);
|
|
k_header->AddInput(sub);
|
|
PerformInductionVarAnalysis();
|
|
|
|
EXPECT_STREQ("wrap((0), (( - (1)) * i + (100)):Int32):Int32",
|
|
GetInductionInfo(k_header, 0).c_str());
|
|
EXPECT_STREQ("wrap((0), (( - (1)) * i + (100)):Int32):Int32",
|
|
GetInductionInfo(store->InputAt(1), 0).c_str());
|
|
EXPECT_STREQ("(( - (1)) * i + (100)):Int32", GetInductionInfo(sub, 0).c_str());
|
|
}
|
|
|
|
TEST_F(InductionVarAnalysisTest, FindSecondOrderWrapAroundInduction) {
|
|
// Setup:
|
|
// k = 0;
|
|
// t = 100;
|
|
// for (int i = 0; i < 100; i++) {
|
|
// a[k] = 0;
|
|
// k = t;
|
|
// t = 100 - i;
|
|
// }
|
|
BuildLoopNest(1);
|
|
HPhi* k_header = InsertLoopPhi(0, 0);
|
|
k_header->AddInput(constant0_);
|
|
HPhi* t = InsertLoopPhi(1, 0);
|
|
t->AddInput(constant100_);
|
|
|
|
HInstruction* store = InsertArrayStore(k_header, 0);
|
|
k_header->AddInput(t);
|
|
HInstruction* sub = InsertInstruction(
|
|
new (GetAllocator()) HSub(DataType::Type::kInt32, constant100_, basic_[0], 0), 0);
|
|
t->AddInput(sub);
|
|
PerformInductionVarAnalysis();
|
|
|
|
EXPECT_STREQ("wrap((0), wrap((100), (( - (1)) * i + (100)):Int32):Int32):Int32",
|
|
GetInductionInfo(store->InputAt(1), 0).c_str());
|
|
}
|
|
|
|
TEST_F(InductionVarAnalysisTest, FindWrapAroundDerivedInduction) {
|
|
// Setup:
|
|
// k = 0;
|
|
// for (int i = 0; i < 100; i++) {
|
|
// t = k + 100;
|
|
// t = k - 100;
|
|
// t = k * 100;
|
|
// t = k << 1;
|
|
// t = - k;
|
|
// k = i << 1;
|
|
// t = - k;
|
|
// }
|
|
BuildLoopNest(1);
|
|
HPhi* k_header = InsertLoopPhi(0, 0);
|
|
k_header->AddInput(constant0_);
|
|
|
|
HInstruction* add = InsertInstruction(
|
|
new (GetAllocator()) HAdd(DataType::Type::kInt32, k_header, constant100_), 0);
|
|
HInstruction* sub = InsertInstruction(
|
|
new (GetAllocator()) HSub(DataType::Type::kInt32, k_header, constant100_), 0);
|
|
HInstruction* mul = InsertInstruction(
|
|
new (GetAllocator()) HMul(DataType::Type::kInt32, k_header, constant100_), 0);
|
|
HInstruction* shl1 = InsertInstruction(
|
|
new (GetAllocator()) HShl(DataType::Type::kInt32, k_header, constant1_), 0);
|
|
HInstruction* neg1 = InsertInstruction(
|
|
new (GetAllocator()) HNeg(DataType::Type::kInt32, k_header), 0);
|
|
HInstruction* shl2 = InsertInstruction(
|
|
new (GetAllocator()) HShl(DataType::Type::kInt32, basic_[0], constant1_), 0);
|
|
HInstruction* neg2 = InsertInstruction(
|
|
new (GetAllocator()) HNeg(DataType::Type::kInt32, shl2), 0);
|
|
k_header->AddInput(shl2);
|
|
PerformInductionVarAnalysis();
|
|
|
|
EXPECT_STREQ("wrap((100), ((2) * i + (100)):Int32):Int32",
|
|
GetInductionInfo(add, 0).c_str());
|
|
EXPECT_STREQ("wrap(((0) - (100)), ((2) * i + ((0) - (100))):Int32):Int32",
|
|
GetInductionInfo(sub, 0).c_str());
|
|
EXPECT_STREQ("wrap((0), (((2) * (100)) * i + (0)):Int32):Int32",
|
|
GetInductionInfo(mul, 0).c_str());
|
|
EXPECT_STREQ("wrap((0), (((2) * (2)) * i + (0)):Int32):Int32",
|
|
GetInductionInfo(shl1, 0).c_str());
|
|
EXPECT_STREQ("wrap((0), (( - (2)) * i + (0)):Int32):Int32",
|
|
GetInductionInfo(neg1, 0).c_str());
|
|
EXPECT_STREQ("((2) * i + (0)):Int32", GetInductionInfo(shl2, 0).c_str());
|
|
EXPECT_STREQ("(( - (2)) * i + (0)):Int32", GetInductionInfo(neg2, 0).c_str());
|
|
}
|
|
|
|
TEST_F(InductionVarAnalysisTest, FindPeriodicInduction) {
|
|
// Setup:
|
|
// k = 0;
|
|
// t = 100;
|
|
// for (int i = 0; i < 100; i++) {
|
|
// a[k] = 0;
|
|
// a[t] = 0;
|
|
// // Swap t <-> k.
|
|
// d = t;
|
|
// t = k;
|
|
// k = d;
|
|
// }
|
|
BuildLoopNest(1);
|
|
HPhi* k_header = InsertLoopPhi(0, 0);
|
|
k_header->AddInput(constant0_);
|
|
HPhi* t = InsertLoopPhi(1, 0);
|
|
t->AddInput(constant100_);
|
|
|
|
HInstruction* store1 = InsertArrayStore(k_header, 0);
|
|
HInstruction* store2 = InsertArrayStore(t, 0);
|
|
k_header->AddInput(t);
|
|
t->AddInput(k_header);
|
|
PerformInductionVarAnalysis();
|
|
|
|
EXPECT_STREQ("periodic((0), (100)):Int32", GetInductionInfo(store1->InputAt(1), 0).c_str());
|
|
EXPECT_STREQ("periodic((100), (0)):Int32", GetInductionInfo(store2->InputAt(1), 0).c_str());
|
|
}
|
|
|
|
TEST_F(InductionVarAnalysisTest, FindIdiomaticPeriodicInduction) {
|
|
// Setup:
|
|
// k = 0;
|
|
// for (int i = 0; i < 100; i++) {
|
|
// a[k] = 0;
|
|
// k = 1 - k;
|
|
// }
|
|
BuildLoopNest(1);
|
|
HPhi* k_header = InsertLoopPhi(0, 0);
|
|
k_header->AddInput(constant0_);
|
|
|
|
HInstruction* store = InsertArrayStore(k_header, 0);
|
|
HInstruction* sub = InsertInstruction(
|
|
new (GetAllocator()) HSub(DataType::Type::kInt32, constant1_, k_header), 0);
|
|
k_header->AddInput(sub);
|
|
PerformInductionVarAnalysis();
|
|
|
|
EXPECT_STREQ("periodic((0), (1)):Int32", GetInductionInfo(store->InputAt(1), 0).c_str());
|
|
EXPECT_STREQ("periodic((1), (0)):Int32", GetInductionInfo(sub, 0).c_str());
|
|
}
|
|
|
|
TEST_F(InductionVarAnalysisTest, FindXorPeriodicInduction) {
|
|
// Setup:
|
|
// k = 0;
|
|
// for (int i = 0; i < 100; i++) {
|
|
// a[k] = 0;
|
|
// k = k ^ 1;
|
|
// }
|
|
BuildLoopNest(1);
|
|
HPhi* k_header = InsertLoopPhi(0, 0);
|
|
k_header->AddInput(constant0_);
|
|
|
|
HInstruction* store = InsertArrayStore(k_header, 0);
|
|
HInstruction* x = InsertInstruction(
|
|
new (GetAllocator()) HXor(DataType::Type::kInt32, k_header, constant1_), 0);
|
|
k_header->AddInput(x);
|
|
PerformInductionVarAnalysis();
|
|
|
|
EXPECT_STREQ("periodic((0), (1)):Int32", GetInductionInfo(store->InputAt(1), 0).c_str());
|
|
EXPECT_STREQ("periodic((1), (0)):Int32", GetInductionInfo(x, 0).c_str());
|
|
}
|
|
|
|
TEST_F(InductionVarAnalysisTest, FindXorConstantLeftPeriodicInduction) {
|
|
// Setup:
|
|
// k = 1;
|
|
// for (int i = 0; i < 100; i++) {
|
|
// k = 1 ^ k;
|
|
// }
|
|
BuildLoopNest(1);
|
|
HPhi* k_header = InsertLoopPhi(0, 0);
|
|
k_header->AddInput(constant1_);
|
|
|
|
HInstruction* x = InsertInstruction(
|
|
new (GetAllocator()) HXor(DataType::Type::kInt32, constant1_, k_header), 0);
|
|
k_header->AddInput(x);
|
|
PerformInductionVarAnalysis();
|
|
|
|
EXPECT_STREQ("periodic((1), ((1) ^ (1))):Int32", GetInductionInfo(k_header, 0).c_str());
|
|
EXPECT_STREQ("periodic(((1) ^ (1)), (1)):Int32", GetInductionInfo(x, 0).c_str());
|
|
}
|
|
|
|
TEST_F(InductionVarAnalysisTest, FindXor100PeriodicInduction) {
|
|
// Setup:
|
|
// k = 1;
|
|
// for (int i = 0; i < 100; i++) {
|
|
// k = k ^ 100;
|
|
// }
|
|
BuildLoopNest(1);
|
|
HPhi* k_header = InsertLoopPhi(0, 0);
|
|
k_header->AddInput(constant1_);
|
|
|
|
HInstruction* x = InsertInstruction(
|
|
new (GetAllocator()) HXor(DataType::Type::kInt32, k_header, constant100_), 0);
|
|
k_header->AddInput(x);
|
|
PerformInductionVarAnalysis();
|
|
|
|
EXPECT_STREQ("periodic((1), ((1) ^ (100))):Int32", GetInductionInfo(k_header, 0).c_str());
|
|
EXPECT_STREQ("periodic(((1) ^ (100)), (1)):Int32", GetInductionInfo(x, 0).c_str());
|
|
}
|
|
|
|
TEST_F(InductionVarAnalysisTest, FindBooleanEqPeriodicInduction) {
|
|
// Setup:
|
|
// k = 0;
|
|
// for (int i = 0; i < 100; i++) {
|
|
// k = (k == 0);
|
|
// }
|
|
BuildLoopNest(1);
|
|
HPhi* k_header = InsertLoopPhi(0, 0);
|
|
k_header->AddInput(constant0_);
|
|
|
|
HInstruction* x = InsertInstruction(new (GetAllocator()) HEqual(k_header, constant0_), 0);
|
|
k_header->AddInput(x);
|
|
PerformInductionVarAnalysis();
|
|
|
|
EXPECT_STREQ("periodic((0), (1)):Bool", GetInductionInfo(k_header, 0).c_str());
|
|
EXPECT_STREQ("periodic((1), (0)):Bool", GetInductionInfo(x, 0).c_str());
|
|
}
|
|
|
|
TEST_F(InductionVarAnalysisTest, FindBooleanEqConstantLeftPeriodicInduction) {
|
|
// Setup:
|
|
// k = 0;
|
|
// for (int i = 0; i < 100; i++) {
|
|
// k = (0 == k);
|
|
// }
|
|
BuildLoopNest(1);
|
|
HPhi* k_header = InsertLoopPhi(0, 0);
|
|
k_header->AddInput(constant0_);
|
|
|
|
HInstruction* x = InsertInstruction(new (GetAllocator()) HEqual(constant0_, k_header), 0);
|
|
k_header->AddInput(x);
|
|
PerformInductionVarAnalysis();
|
|
|
|
EXPECT_STREQ("periodic((0), (1)):Bool", GetInductionInfo(k_header, 0).c_str());
|
|
EXPECT_STREQ("periodic((1), (0)):Bool", GetInductionInfo(x, 0).c_str());
|
|
}
|
|
|
|
TEST_F(InductionVarAnalysisTest, FindBooleanNePeriodicInduction) {
|
|
// Setup:
|
|
// k = 0;
|
|
// for (int i = 0; i < 100; i++) {
|
|
// k = (k != 1);
|
|
// }
|
|
BuildLoopNest(1);
|
|
HPhi* k_header = InsertLoopPhi(0, 0);
|
|
k_header->AddInput(constant0_);
|
|
|
|
HInstruction* x = InsertInstruction(new (GetAllocator()) HNotEqual(k_header, constant1_), 0);
|
|
k_header->AddInput(x);
|
|
PerformInductionVarAnalysis();
|
|
|
|
EXPECT_STREQ("periodic((0), (1)):Bool", GetInductionInfo(k_header, 0).c_str());
|
|
EXPECT_STREQ("periodic((1), (0)):Bool", GetInductionInfo(x, 0).c_str());
|
|
}
|
|
|
|
TEST_F(InductionVarAnalysisTest, FindBooleanNeConstantLeftPeriodicInduction) {
|
|
// Setup:
|
|
// k = 0;
|
|
// for (int i = 0; i < 100; i++) {
|
|
// k = (1 != k);
|
|
// }
|
|
BuildLoopNest(1);
|
|
HPhi* k_header = InsertLoopPhi(0, 0);
|
|
k_header->AddInput(constant0_);
|
|
|
|
HInstruction* x = InsertInstruction(new (GetAllocator()) HNotEqual(constant1_, k_header), 0);
|
|
k_header->AddInput(x);
|
|
PerformInductionVarAnalysis();
|
|
|
|
EXPECT_STREQ("periodic((0), (1)):Bool", GetInductionInfo(k_header, 0).c_str());
|
|
EXPECT_STREQ("periodic((1), (0)):Bool", GetInductionInfo(x, 0).c_str());
|
|
}
|
|
|
|
TEST_F(InductionVarAnalysisTest, FindDerivedPeriodicInduction) {
|
|
// Setup:
|
|
// k = 0;
|
|
// for (int i = 0; i < 100; i++) {
|
|
// t = - k;
|
|
// k = 1 - k;
|
|
// t = k + 100;
|
|
// t = k - 100;
|
|
// t = k * 100;
|
|
// t = k << 1;
|
|
// t = - k;
|
|
// }
|
|
BuildLoopNest(1);
|
|
HPhi* k_header = InsertLoopPhi(0, 0);
|
|
k_header->AddInput(constant0_);
|
|
|
|
HInstruction* neg1 = InsertInstruction(
|
|
new (GetAllocator()) HNeg(DataType::Type::kInt32, k_header), 0);
|
|
HInstruction* idiom = InsertInstruction(
|
|
new (GetAllocator()) HSub(DataType::Type::kInt32, constant1_, k_header), 0);
|
|
HInstruction* add = InsertInstruction(
|
|
new (GetAllocator()) HAdd(DataType::Type::kInt32, idiom, constant100_), 0);
|
|
HInstruction* sub = InsertInstruction(
|
|
new (GetAllocator()) HSub(DataType::Type::kInt32, idiom, constant100_), 0);
|
|
HInstruction* mul = InsertInstruction(
|
|
new (GetAllocator()) HMul(DataType::Type::kInt32, idiom, constant100_), 0);
|
|
HInstruction* shl = InsertInstruction(
|
|
new (GetAllocator()) HShl(DataType::Type::kInt32, idiom, constant1_), 0);
|
|
HInstruction* neg2 = InsertInstruction(
|
|
new (GetAllocator()) HNeg(DataType::Type::kInt32, idiom), 0);
|
|
k_header->AddInput(idiom);
|
|
PerformInductionVarAnalysis();
|
|
|
|
EXPECT_STREQ("periodic((0), (1)):Int32", GetInductionInfo(k_header, 0).c_str());
|
|
EXPECT_STREQ("periodic((0), ( - (1))):Int32", GetInductionInfo(neg1, 0).c_str());
|
|
EXPECT_STREQ("periodic((1), (0)):Int32", GetInductionInfo(idiom, 0).c_str());
|
|
EXPECT_STREQ("periodic(((1) + (100)), (100)):Int32", GetInductionInfo(add, 0).c_str());
|
|
EXPECT_STREQ("periodic(((1) - (100)), ((0) - (100))):Int32", GetInductionInfo(sub, 0).c_str());
|
|
EXPECT_STREQ("periodic((100), (0)):Int32", GetInductionInfo(mul, 0).c_str());
|
|
EXPECT_STREQ("periodic((2), (0)):Int32", GetInductionInfo(shl, 0).c_str());
|
|
EXPECT_STREQ("periodic(( - (1)), (0)):Int32", GetInductionInfo(neg2, 0).c_str());
|
|
}
|
|
|
|
TEST_F(InductionVarAnalysisTest, FindDeepLoopInduction) {
|
|
// Setup:
|
|
// k = 0;
|
|
// for (int i_0 = 0; i_0 < 100; i_0++) {
|
|
// ..
|
|
// for (int i_9 = 0; i_9 < 100; i_9++) {
|
|
// k = 1 + k;
|
|
// a[k] = 0;
|
|
// }
|
|
// ..
|
|
// }
|
|
BuildLoopNest(10);
|
|
|
|
HPhi* k_header[10];
|
|
for (int d = 0; d < 10; d++) {
|
|
k_header[d] = InsertLoopPhi(0, d);
|
|
}
|
|
|
|
HInstruction* inc = InsertInstruction(
|
|
new (GetAllocator()) HAdd(DataType::Type::kInt32, constant1_, k_header[9]), 9);
|
|
HInstruction* store = InsertArrayStore(inc, 9);
|
|
|
|
for (int d = 0; d < 10; d++) {
|
|
k_header[d]->AddInput((d != 0) ? k_header[d - 1] : constant0_);
|
|
k_header[d]->AddInput((d != 9) ? k_header[d + 1] : inc);
|
|
}
|
|
PerformInductionVarAnalysis();
|
|
|
|
// Avoid exact phi number, since that depends on the SSA building phase.
|
|
std::regex r("\\(\\(1\\) \\* i \\+ "
|
|
"\\(\\(1\\) \\+ \\(\\d+:Phi\\)\\)\\):Int32");
|
|
|
|
for (int d = 0; d < 10; d++) {
|
|
if (d == 9) {
|
|
EXPECT_TRUE(std::regex_match(GetInductionInfo(store->InputAt(1), d), r));
|
|
} else {
|
|
EXPECT_STREQ("", GetInductionInfo(store->InputAt(1), d).c_str());
|
|
}
|
|
EXPECT_STREQ("((1) * i + (1)):Int32", GetInductionInfo(increment_[d], d).c_str());
|
|
// Trip-count.
|
|
EXPECT_STREQ("((100) (TC-loop) ((0) < (100)))", GetTripCount(d).c_str());
|
|
}
|
|
}
|
|
|
|
TEST_F(InductionVarAnalysisTest, ByteInductionIntLoopControl) {
|
|
// Setup:
|
|
// for (int i = 0; i < 100; i++) {
|
|
// k = (byte) i;
|
|
// a[k] = 0;
|
|
// a[i] = 0;
|
|
// }
|
|
BuildLoopNest(1);
|
|
HInstruction* conv = InsertInstruction(
|
|
new (GetAllocator()) HTypeConversion(DataType::Type::kInt8, 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).
|
|
EXPECT_STREQ("((1) * i + (0)):Int8", GetInductionInfo(store1->InputAt(1), 0).c_str());
|
|
EXPECT_STREQ("((1) * i + (0)):Int32", GetInductionInfo(store2->InputAt(1), 0).c_str());
|
|
EXPECT_STREQ("((1) * i + (1)):Int32", 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)));
|
|
|
|
// Trip-count.
|
|
EXPECT_STREQ("((100) (TC-loop) ((0) < (100)))", GetTripCount(0).c_str());
|
|
}
|
|
|
|
TEST_F(InductionVarAnalysisTest, ByteInductionDerivedIntLoopControl) {
|
|
// Setup:
|
|
// for (int i = 0; i < 100; i++) {
|
|
// k = (byte) i;
|
|
// a[k] = 0;
|
|
// k = k + 1
|
|
// a[k] = 0;
|
|
// }
|
|
BuildLoopNest(1);
|
|
HInstruction* conv = InsertInstruction(
|
|
new (GetAllocator()) HTypeConversion(DataType::Type::kInt8, basic_[0], kNoDexPc), 0);
|
|
HInstruction* store1 = InsertArrayStore(conv, 0);
|
|
HInstruction* add = InsertInstruction(
|
|
new (GetAllocator()) HAdd(DataType::Type::kInt32, conv, constant1_), 0);
|
|
HInstruction* store2 = InsertArrayStore(add, 0);
|
|
|
|
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 + (0)):Int8", GetInductionInfo(store1->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 (GetAllocator()) HAdd(DataType::Type::kInt32, k_header, constant1_), 0);
|
|
HInstruction* conv = InsertInstruction(
|
|
new (GetAllocator()) HTypeConversion(DataType::Type::kInt8, 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)):Int8", 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 (GetAllocator()) HAdd(DataType::Type::kInt32, k_header, constant1_), 0);
|
|
HInstruction* conv = InsertInstruction(
|
|
new (GetAllocator()) HTypeConversion(DataType::Type::kInt8, 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 (GetAllocator()) HTypeConversion(DataType::Type::kInt8, k_header, kNoDexPc), 0);
|
|
HInstruction* add = InsertInstruction(
|
|
new (GetAllocator()) HAdd(DataType::Type::kInt32, 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) {
|
|
// 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 (GetAllocator()) HTypeConversion(DataType::Type::kInt8, increment_[0], kNoDexPc);
|
|
loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
|
|
basic_[0]->ReplaceInput(conv, 1);
|
|
PerformInductionVarAnalysis();
|
|
|
|
// Recorded at the phi, but not transferred to increment.
|
|
EXPECT_STREQ("((1) * i + (-128)):Int8", 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());
|
|
}
|
|
|
|
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 (GetAllocator()) HTypeConversion(DataType::Type::kInt8, increment_[0], kNoDexPc);
|
|
loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
|
|
basic_[0]->ReplaceInput(conv, 1);
|
|
PerformInductionVarAnalysis();
|
|
|
|
// Recorded at the phi, but not transferred to increment.
|
|
EXPECT_STREQ("((1) * i + (-128)):Int8", 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());
|
|
}
|
|
|
|
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 (GetAllocator()) HTypeConversion(DataType::Type::kInt16, increment_[0], kNoDexPc);
|
|
loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
|
|
basic_[0]->ReplaceInput(conv, 1);
|
|
PerformInductionVarAnalysis();
|
|
|
|
// Recorded at the phi, but not transferred to increment.
|
|
EXPECT_STREQ("((1) * i + (-32768)):Int16", 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());
|
|
}
|
|
|
|
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 (GetAllocator()) HTypeConversion(DataType::Type::kInt16, increment_[0], kNoDexPc);
|
|
loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
|
|
basic_[0]->ReplaceInput(conv, 1);
|
|
PerformInductionVarAnalysis();
|
|
|
|
// Recorded at the phi, but not transferred to increment.
|
|
EXPECT_STREQ("((1) * i + (-32768)):Int16", 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());
|
|
}
|
|
|
|
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 (GetAllocator()) HTypeConversion(DataType::Type::kUint16, increment_[0], kNoDexPc);
|
|
loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
|
|
basic_[0]->ReplaceInput(conv, 1);
|
|
PerformInductionVarAnalysis();
|
|
|
|
// Recorded at the phi, but not transferred to increment.
|
|
EXPECT_STREQ("((1) * i + (0)):Uint16", 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());
|
|
}
|
|
|
|
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 (GetAllocator()) HTypeConversion(DataType::Type::kUint16, increment_[0], kNoDexPc);
|
|
loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
|
|
basic_[0]->ReplaceInput(conv, 1);
|
|
PerformInductionVarAnalysis();
|
|
|
|
// Recorded at the phi, but not transferred to increment.
|
|
EXPECT_STREQ("((1) * i + (0)):Uint16", 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());
|
|
}
|
|
|
|
} // namespace art
|