Files
sdk/runtime/vm/compiler/backend/loops_test.cc
Vyacheslav Egorov 6daf74f845 [vm/compiler] Unbox more integer phis on 32bit targets
Replace previous more conservative heuristic with a new one which
unboxes all the phis which have at least one unboxed input and
at least one unboxed use. Additionally the new heuristic looks
through phis transitively when looking for inputs and uses.

TEST=ci

Cq-Include-Trybots: luci.dart.try:vm-kernel-precomp-linux-debug-simarm_x64-try,vm-kernel-precomp-linux-release-simarm-try,vm-kernel-precomp-linux-release-simarm64-try
Change-Id: I5cb3280187fb29be77400e9fc4ead1cb6a6c3748
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/224202
Reviewed-by: Alexander Markov <alexmarkov@google.com>
Commit-Queue: Slava Egorov <vegorov@google.com>
2022-01-04 11:57:45 +00:00

513 lines
13 KiB
C++

// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
//
// Unit tests specific to loops and induction variables.
// Note, try to avoid relying on information that is subject
// to change (block ids, variable numbers, etc.) in order
// to make this test less sensitive to unrelated changes.
#include "vm/compiler/backend/loops.h"
#include "vm/compiler/backend/il_printer.h"
#include "vm/compiler/backend/il_test_helper.h"
#include "vm/compiler/backend/inliner.h"
#include "vm/compiler/backend/type_propagator.h"
#include "vm/compiler/compiler_pass.h"
#include "vm/compiler/frontend/kernel_to_il.h"
#include "vm/compiler/jit/jit_call_specializer.h"
#include "vm/log.h"
#include "vm/object.h"
#include "vm/parser.h"
#include "vm/symbols.h"
#include "vm/unit_test.h"
namespace dart {
// Helper method to construct an induction debug string for loop hierarchy.
void TestString(BaseTextBuffer* f,
LoopInfo* loop,
const GrowableArray<BlockEntryInstr*>& preorder) {
for (; loop != nullptr; loop = loop->next()) {
intptr_t depth = loop->NestingDepth();
f->Printf("%*c[%" Pd "\n", static_cast<int>(2 * depth), ' ', loop->id());
for (BitVector::Iterator block_it(loop->blocks()); !block_it.Done();
block_it.Advance()) {
BlockEntryInstr* block = preorder[block_it.Current()];
if (block->IsJoinEntry()) {
for (PhiIterator it(block->AsJoinEntry()); !it.Done(); it.Advance()) {
InductionVar* induc = loop->LookupInduction(it.Current());
if (induc != nullptr) {
// Obtain the debug string for induction and bounds.
f->Printf("%*c%s", static_cast<int>(2 * depth), ' ',
induc->ToCString());
for (auto bound : induc->bounds()) {
f->Printf(" %s", bound.limit_->ToCString());
}
f->AddString("\n");
}
}
}
for (ForwardInstructionIterator it(block); !it.Done(); it.Advance()) {
InductionVar* induc =
loop->LookupInduction(it.Current()->AsDefinition());
if (InductionVar::IsInduction(induc)) {
f->Printf("%*c%s\n", static_cast<int>(2 * depth), ' ',
induc->ToCString());
}
}
}
TestString(f, loop->inner(), preorder);
f->Printf("%*c]\n", static_cast<int>(2 * depth), ' ');
}
}
// Helper method to build CFG and compute induction.
static const char* ComputeInduction(Thread* thread, const char* script_chars) {
// Load the script and exercise the code once.
const auto& root_library = Library::Handle(LoadTestScript(script_chars));
Invoke(root_library, "main");
std::initializer_list<CompilerPass::Id> passes = {
CompilerPass::kComputeSSA,
CompilerPass::kTypePropagation,
CompilerPass::kApplyICData,
CompilerPass::kTypePropagation,
CompilerPass::kSelectRepresentations,
CompilerPass::kTypePropagation,
CompilerPass::kCanonicalize,
};
const auto& function = Function::Handle(GetFunction(root_library, "foo"));
TestPipeline pipeline(function, CompilerPass::kJIT);
FlowGraph* flow_graph = pipeline.RunPasses(passes);
// Build loop hierarchy and find induction.
const LoopHierarchy& hierarchy = flow_graph->GetLoopHierarchy();
hierarchy.ComputeInduction();
flow_graph->RemoveRedefinitions(); // don't query later
// Construct and return a debug string for testing.
char buffer[1024];
BufferFormatter f(buffer, sizeof(buffer));
TestString(&f, hierarchy.top(), flow_graph->preorder());
return Thread::Current()->zone()->MakeCopyOfString(buffer);
}
//
// Induction tests.
//
ISOLATE_UNIT_TEST_CASE(BasicInductionUp) {
const char* script_chars =
R"(
foo() {
for (int i = 0; i < 100; i++) {
}
}
main() {
foo();
}
)";
const char* expected =
" [0\n"
" LIN(0 + 1 * i) 100\n" // phi
" LIN(1 + 1 * i)\n" // add
" ]\n";
EXPECT_STREQ(expected, ComputeInduction(thread, script_chars));
}
ISOLATE_UNIT_TEST_CASE(BasicInductionDown) {
const char* script_chars =
R"(
foo() {
for (int i = 100; i > 0; i--) {
}
}
main() {
foo();
}
)";
const char* expected =
" [0\n"
" LIN(100 + -1 * i) 0\n" // phi
" LIN(99 + -1 * i)\n" // sub
" ]\n";
EXPECT_STREQ(expected, ComputeInduction(thread, script_chars));
}
ISOLATE_UNIT_TEST_CASE(BasicInductionStepUp) {
const char* script_chars =
R"(
foo() {
for (int i = 10; i < 100; i += 2) {
}
}
main() {
foo();
}
)";
const char* expected =
" [0\n"
" LIN(10 + 2 * i)\n" // phi
" LIN(12 + 2 * i)\n" // add
" ]\n";
EXPECT_STREQ(expected, ComputeInduction(thread, script_chars));
}
ISOLATE_UNIT_TEST_CASE(BasicInductionStepDown) {
const char* script_chars =
R"(
foo() {
for (int i = 100; i >= 0; i -= 7) {
}
}
main() {
foo();
}
)";
const char* expected =
" [0\n"
" LIN(100 + -7 * i)\n" // phi
" LIN(93 + -7 * i)\n" // sub
" ]\n";
EXPECT_STREQ(expected, ComputeInduction(thread, script_chars));
}
ISOLATE_UNIT_TEST_CASE(BasicInductionLoopNest) {
const char* script_chars =
R"(
foo() {
for (int i = 0; i < 100; i++) {
for (int j = 1; j < 100; j++) {
for (int k = 2; k < 100; k++) {
}
}
}
}
main() {
foo();
}
)";
const char* expected =
" [2\n"
" LIN(0 + 1 * i) 100\n" // i
" LIN(1 + 1 * i)\n"
" [1\n"
" LIN(1 + 1 * i) 100\n" // j
" LIN(2 + 1 * i)\n"
" [0\n"
" LIN(2 + 1 * i) 100\n" // k
" LIN(3 + 1 * i)\n"
" ]\n"
" ]\n"
" ]\n";
EXPECT_STREQ(expected, ComputeInduction(thread, script_chars));
}
ISOLATE_UNIT_TEST_CASE(ChainInduction) {
const char* script_chars =
R"(
foo() {
int j = 1;
for (int i = 0; i < 100; i++) {
j += 5;
j += 7;
}
}
main() {
foo();
}
)";
const char* expected =
" [0\n"
" LIN(1 + 12 * i)\n" // phi (j)
" LIN(0 + 1 * i) 100\n" // phi
" LIN(6 + 12 * i)\n" // j-add
" LIN(13 + 12 * i)\n" // j-add
" LIN(1 + 1 * i)\n" // add
" ]\n";
EXPECT_STREQ(expected, ComputeInduction(thread, script_chars));
}
ISOLATE_UNIT_TEST_CASE(TwoWayInduction) {
const char* script_chars =
R"(
foo() {
int j = 123;
for (int i = 0; i < 100; i++) {
if (i == 10) {
j += 3;
} else {
j += 3;
}
}
}
main() {
foo();
}
)";
const char* expected =
" [0\n"
" LIN(123 + 3 * i)\n" // phi (j)
" LIN(0 + 1 * i) 100\n" // phi
" LIN(126 + 3 * i)\n" // j-true
" LIN(126 + 3 * i)\n" // j-false
" LIN(1 + 1 * i)\n" // add
" LIN(126 + 3 * i)\n" // phi (j)
" ]\n";
EXPECT_STREQ(expected, ComputeInduction(thread, script_chars));
}
ISOLATE_UNIT_TEST_CASE(DerivedInduction) {
const char* script_chars =
R"(
foo() {
for (int i = 1; i < 100; i++) {
int a = i + 3;
int b = i - 5;
int c = i * 7;
int d = - i;
}
}
main() {
foo();
}
)";
const char* expected =
" [0\n"
" LIN(1 + 1 * i) 100\n" // phi
" LIN(4 + 1 * i)\n" // a
" LIN(-4 + 1 * i)\n" // b
" LIN(7 + 7 * i)\n" // c
" LIN(-1 + -1 * i)\n" // d
" LIN(2 + 1 * i)\n" // add
" ]\n";
EXPECT_STREQ(expected, ComputeInduction(thread, script_chars));
}
ISOLATE_UNIT_TEST_CASE(WrapAroundAndDerived) {
const char* script_chars =
R"(
foo() {
int w = 99;
for (int i = 0; i < 100; i++) {
int a = w + 3;
int b = w - 5;
int c = w * 7;
int d = - w;
w = i;
}
}
main() {
foo();
}
)";
const char* expected =
" [0\n"
" WRAP(99, LIN(0 + 1 * i))\n" // phi (w)
" LIN(0 + 1 * i) 100\n" // phi (i)
" WRAP(102, LIN(3 + 1 * i))\n" // a
" WRAP(94, LIN(-5 + 1 * i))\n" // b
" WRAP(693, LIN(0 + 7 * i))\n" // c
" WRAP(-99, LIN(0 + -1 * i))\n" // d
" LIN(1 + 1 * i)\n" // add
" ]\n";
EXPECT_STREQ(expected, ComputeInduction(thread, script_chars));
}
ISOLATE_UNIT_TEST_CASE(PeriodicAndDerived) {
const char* script_chars =
R"(
foo() {
int p1 = 3;
int p2 = 5;
for (int i = 0; i < 100; i++) {
int a = p1 + 3;
int b = p1 - 5;
int c = p1 * 7;
int d = - p1;
p1 = - p1;
p2 = 100 - p2;
}
}
main() {
foo();
}
)";
const char* expected =
" [0\n"
" PERIOD(3, -3)\n" // phi(p1)
" PERIOD(5, 95)\n" // phi(p2)
" LIN(0 + 1 * i) 100\n" // phi
" PERIOD(6, 0)\n" // a
" PERIOD(-2, -8)\n" // b
" PERIOD(21, -21)\n" // c
" PERIOD(-3, 3)\n" // d
" PERIOD(-3, 3)\n" // p1
" PERIOD(95, 5)\n" // p2
" LIN(1 + 1 * i)\n" // add
" ]\n";
EXPECT_STREQ(expected, ComputeInduction(thread, script_chars));
}
//
// Bound specific tests.
//
ISOLATE_UNIT_TEST_CASE(NonStrictConditionUp) {
const char* script_chars =
R"(
foo() {
for (int i = 0; i <= 100; i++) {
}
}
main() {
foo();
}
)";
const char* expected =
" [0\n"
" LIN(0 + 1 * i) 101\n" // phi
" LIN(1 + 1 * i)\n" // add
" ]\n";
EXPECT_STREQ(expected, ComputeInduction(thread, script_chars));
}
ISOLATE_UNIT_TEST_CASE(NonStrictConditionUpWrap) {
const char* script_chars =
R"(
foo() {
for (int i = 0x7ffffffffffffffe; i <= 0x7fffffffffffffff; i++) {
if (i < 0) break;
}
}
main() {
foo();
}
)";
const char* expected =
" [0\n"
" LIN(9223372036854775806 + 1 * i)\n" // phi
" LIN(9223372036854775807 + 1 * i)\n" // add
" ]\n";
EXPECT_STREQ(expected, ComputeInduction(thread, script_chars));
}
ISOLATE_UNIT_TEST_CASE(NonStrictConditionDown) {
const char* script_chars =
R"(
foo() {
for (int i = 100; i >= 0; i--) {
}
}
main() {
foo();
}
)";
const char* expected =
" [0\n"
" LIN(100 + -1 * i) -1\n" // phi
" LIN(99 + -1 * i)\n" // add
" ]\n";
EXPECT_STREQ(expected, ComputeInduction(thread, script_chars));
}
ISOLATE_UNIT_TEST_CASE(NonStrictConditionDownWrap) {
const char* script_chars =
R"(
foo() {
for (int i = 0x8000000000000001; i >= 0x8000000000000000; i--) {
if (i > 0) break;
}
}
main() {
foo();
}
)";
const char* expected =
" [0\n"
" LIN(-9223372036854775807 + -1 * i)\n" // phi
" LIN(-9223372036854775808 + -1 * i)\n" // sub
" ]\n";
EXPECT_STREQ(expected, ComputeInduction(thread, script_chars));
}
ISOLATE_UNIT_TEST_CASE(NotEqualConditionUp) {
const char* script_chars =
R"(
foo() {
for (int i = 10; i != 20; i++) {
}
}
main() {
foo();
}
)";
const char* expected =
" [0\n"
" LIN(10 + 1 * i) 20\n" // phi
" LIN(11 + 1 * i)\n" // add
" ]\n";
EXPECT_STREQ(expected, ComputeInduction(thread, script_chars));
}
ISOLATE_UNIT_TEST_CASE(NotEqualConditionDown) {
const char* script_chars =
R"(
foo() {
for (int i = 20; i != 10; i--) {
}
}
main() {
foo();
}
)";
const char* expected =
" [0\n"
" LIN(20 + -1 * i) 10\n" // phi
" LIN(19 + -1 * i)\n" // sub
" ]\n";
EXPECT_STREQ(expected, ComputeInduction(thread, script_chars));
}
ISOLATE_UNIT_TEST_CASE(SecondExitUp) {
const char* script_chars =
R"(
foo() {
for (int i = 0; i < 100; i++) {
if (i >= 50) break;
}
}
main() {
foo();
}
)";
const char* expected =
" [0\n"
" LIN(0 + 1 * i) 100 50\n" // phi
" LIN(1 + 1 * i)\n" // add
" ]\n";
EXPECT_STREQ(expected, ComputeInduction(thread, script_chars));
}
ISOLATE_UNIT_TEST_CASE(SecondExitDown) {
const char* script_chars =
R"(
foo() {
for (int i = 100; i > 0; i--) {
if (i <= 10) break;
}
}
main() {
foo();
}
)";
const char* expected =
" [0\n"
" LIN(100 + -1 * i) 0 10\n" // phi
" LIN(99 + -1 * i)\n" // sub
" ]\n";
EXPECT_STREQ(expected, ComputeInduction(thread, script_chars));
}
} // namespace dart