blob: 39ffddb92569f24799c8594a718567aa16111d2a [file] [log] [blame]
// Copyright (c) 2019, 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 BCE (bounds check eliminaton),
// which runs as part of range analysis optimizations.
#include "vm/compiler/backend/range_analysis.h"
#include "vm/compiler/backend/il.h"
#include "vm/compiler/backend/il_printer.h"
#include "vm/compiler/backend/il_test_helper.h"
#include "vm/compiler/compiler_pass.h"
#include "vm/object.h"
#include "vm/unit_test.h"
namespace dart {
// Helper method to count number of bounds checks.
static intptr_t CountBoundChecks(FlowGraph* flow_graph) {
intptr_t count = 0;
for (BlockIterator block_it = flow_graph->reverse_postorder_iterator();
!block_it.Done(); block_it.Advance()) {
for (ForwardInstructionIterator it(block_it.Current()); !it.Done();
it.Advance()) {
if (it.Current()->IsCheckBoundBase()) {
count++;
}
}
}
return count;
}
// Helper method to build CFG, run BCE, and count number of
// before/after bounds checks.
static std::pair<intptr_t, intptr_t> ApplyBCE(const char* script_chars,
CompilerPass::PipelineMode mode) {
// Load the script and exercise the code once
// while exercising the given compiler passes.
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::kInlining,
CompilerPass::kTypePropagation,
CompilerPass::kApplyICData,
CompilerPass::kSelectRepresentations,
CompilerPass::kCanonicalize,
CompilerPass::kConstantPropagation,
CompilerPass::kCSE,
CompilerPass::kLICM,
};
const auto& function = Function::Handle(GetFunction(root_library, "foo"));
TestPipeline pipeline(function, mode);
FlowGraph* flow_graph = pipeline.RunPasses(passes);
// Count the number of before/after bounds checks.
const intptr_t num_bc_before = CountBoundChecks(flow_graph);
RangeAnalysis range_analysis(flow_graph);
range_analysis.Analyze();
const intptr_t num_bc_after = CountBoundChecks(flow_graph);
return {num_bc_before, num_bc_after};
}
static void TestScriptJIT(const char* script_chars,
intptr_t expected_before,
intptr_t expected_after) {
auto jit_result = ApplyBCE(script_chars, CompilerPass::kJIT);
EXPECT_EQ(expected_before, jit_result.first);
EXPECT_EQ(expected_after, jit_result.second);
}
//
// BCE (bounds-check-elimination) tests.
//
ISOLATE_UNIT_TEST_CASE(BCECannotRemove) {
const char* kScriptChars =
R"(
import 'dart:typed_data';
foo(Float64List l) {
return l[0];
}
main() {
foo(new Float64List(1));
}
)";
TestScriptJIT(kScriptChars, 1, 1);
}
ISOLATE_UNIT_TEST_CASE(BCERemoveOne) {
const char* kScriptChars =
R"(
import 'dart:typed_data';
foo(Float64List l) {
return l[1] + l[0];
}
main() {
foo(new Float64List(2));
}
)";
TestScriptJIT(kScriptChars, 2, 1);
}
ISOLATE_UNIT_TEST_CASE(BCESimpleLoops) {
const char* kScriptChars =
R"(
import 'dart:typed_data';
foo(Float64List l) {
for (int i = 0; i < l.length; i++) {
l[i] = 0;
}
for (int i = 10; i <= l.length - 5; i++) {
l[i] = 1;
}
}
main() {
foo(new Float64List(100));
}
)";
TestScriptJIT(kScriptChars, 2, 0);
}
ISOLATE_UNIT_TEST_CASE(BCESimpleLoopsDown) {
const char* kScriptChars =
R"(
import 'dart:typed_data';
foo(Float64List l) {
for (int i = l.length - 1; i >= 0; i--) {
l[i] = 0;
}
for (int i = l.length - 5; i >= 10; i--) {
l[i] = 1;
}
}
main() {
foo(new Float64List(100));
}
)";
TestScriptJIT(kScriptChars, 2, 0);
}
ISOLATE_UNIT_TEST_CASE(BCEModulo) {
const char* kScriptChars =
R"(
foo(int i) {
var l = List<int>.filled(3, 0);
return l[i % 3] ?? l[i % (-3)];
}
main() {
foo(0);
}
)";
TestScriptJIT(kScriptChars, 2, 0);
}
ISOLATE_UNIT_TEST_CASE(BCELowerTriangular) {
const char* kScriptChars =
R"(
import 'dart:typed_data';
foo(Float64List l) {
for (int i = 0; i < l.length; i++) {
for (int j = 0; j <= i; j++) {
l[i] += l[j];
}
}
}
main() {
foo(new Float64List(100));
}
)";
TestScriptJIT(kScriptChars, 2, 0);
}
ISOLATE_UNIT_TEST_CASE(BCEUpperTriangular) {
const char* kScriptChars =
R"(
import 'dart:typed_data';
foo(Float64List l) {
for (int i = 0; i < l.length; i++) {
for (int j = i; j < l.length; j++) {
l[i] += l[j];
}
}
}
main() {
foo(new Float64List(100));
}
)";
TestScriptJIT(kScriptChars, 2, 0);
}
ISOLATE_UNIT_TEST_CASE(BCETriangularDown) {
const char* kScriptChars =
R"(
import 'dart:typed_data';
foo(Float64List l) {
for (int i = l.length - 1; i >= 0; i--) {
for (int j = i; j >= 0; j--) {
l[i] += l[j];
}
}
}
main() {
foo(new Float64List(100));
}
)";
TestScriptJIT(kScriptChars, 2, 0);
}
ISOLATE_UNIT_TEST_CASE(BCENamedLength) {
const char* kScriptChars =
R"(
import 'dart:typed_data';
Int8List foo(int count) {
var x = new Int8List(count);
for (int i = 0; i < count; i++) {
x[i] = 0;
}
return x;
}
main() {
foo(100);
}
)";
TestScriptJIT(kScriptChars, 1, 0);
}
ISOLATE_UNIT_TEST_CASE(BCEBubbleSort) {
const char* kScriptChars =
R"(
import 'dart:typed_data';
foo(Float64List a) {
int len = a.length;
for (int i = len - 2; i >= 0; i--) {
for (int j = 0; j <= i; j++) {
var c = a[j];
var n = a[j + 1];
if (c > n) {
a[j] = n;
a[j + 1] = c;
}
}
}
}
main() {
foo(new Float64List(100));
}
)";
TestScriptJIT(kScriptChars, 2, 0);
}
ISOLATE_UNIT_TEST_CASE(BCEArithmeticWrapAround) {
const char* kScriptChars =
R"(
import 'dart:typed_data';
const kMax = 0x7fffffffffffffff;
foo(Float64List a) {
for (int i = kMax - 10; i < kMax; i++) {
for (int j = i + 10; j < a.length; j++) {
// Don't be fooled: j in [-minint, len).
a[j] = 1;
}
}
}
main() {
try {
foo(new Float64List(100));
} catch (e) {
}
}
)";
TestScriptJIT(kScriptChars, 1, 1);
}
ISOLATE_UNIT_TEST_CASE(BCEListNamedAndPlainLength) {
const char* kScriptChars =
R"(
List<int> foo(int count) {
var x = new List<int>.filled(count, 42);
for (int i = 0; i < count; i++) {
x[i] = 0;
}
for (int i = 0; i < x.length; i++) {
x[i] = 0;
}
return x;
}
main() {
foo(100);
}
)";
TestScriptJIT(kScriptChars, 2, 0);
}
} // namespace dart