4f3293c70e
Recognized factories have known result type and its type arguments and length are automatically forwarded (length is forwarded only for fixed-size lists). List.filled factory is recognized only if optional 'growable' parameter is not passed. Fixes https://github.com/dart-lang/sdk/issues/42019 Change-Id: I0d63428f1c4667f3981447a939dbef67170e542e Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/149385 Reviewed-by: Ryan Macnak <rmacnak@google.com> Commit-Queue: Alexander Markov <alexmarkov@google.com>
298 lines
7.3 KiB
C++
298 lines
7.3 KiB
C++
// 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
|