Files
sdk/pkg/compiler/test/codegen/value_range_test.dart
T
Stephen Adams 404edd9435 [dart2js] Fix SSA value range analysis bugs
Treat the loop update marker more like an ordinary symbolic value. The old symbolic marker attempted to do widening 'on the fly', which could lead to incorrect results when the update could reset the value to a constant lower than the starting value. The new version moves the widening to a single place, at the loop update.

Fix #53355 by caching intermediate results so that long chains of diamond control flow are not explored exponentially.

There are very few changes in apps. There is one change in a Flutter app that is like the changed codegen/value_range_test where the bounds check can be eliminated because the loop index may be decremented, but not more than the increment, so is still weakly monotonic. There is one change in a large ACX app where a lower bounds check is no longer removed but I *think* it was previously removed incorrectly, though it is hard to tell since it is in a huge function.

Issue: #48465
Issue: #53078
Issue: #53355
Change-Id: Ib125cd6bb30cef52f8dfcd53eaa13e439f26316c
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/322594
Reviewed-by: Sigmund Cherem <sigmund@google.com>
Commit-Queue: Stephen Adams <sra@google.com>
2023-08-29 00:52:58 +00:00

300 lines
5.5 KiB
Dart

// Copyright (c) 2017, 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.
import 'dart:async';
import 'package:async_helper/async_helper.dart';
import 'package:expect/expect.dart';
import '../helpers/compiler_helper.dart';
const int REMOVED = 0;
const int ABOVE_ZERO = 1;
const int BELOW_LENGTH = 2;
const int KEPT = 3;
const int ONE_CHECK = 4;
const int ONE_ZERO_CHECK = 5;
const int BELOW_ZERO_CHECK = 6;
final List TESTS = [
"""
main(bool check) {
var a = check ? [1] : [1, 2];
var sum = 0;
for (int i = 0; i < a.length; i++) {
sum += a[i];
}
return sum;
}
""",
REMOVED,
"""
main(int value) {
var a = [1, 2];
var sum = 0;
for (int i = 0; i < value; i++) {
sum += a[i];
}
return sum;
}
""",
ABOVE_ZERO,
"""
main(bool check) {
// Make sure value is an int.
var value = check ? 42 : 54;
var a = List.filled(value, 1);
var sum = 0;
for (int i = 0; i < value; i++) {
sum += a[i];
}
return sum;
}
""",
REMOVED,
"""
main() {
var a = [];
return a[0];
}
""",
KEPT,
"""
main() {
var a = [];
return a.removeLast();
}
""",
KEPT,
"""
main() {
var a = List.filled(4, null);
return a[0];
}
""",
REMOVED,
"""
main() {
var a = List.filled(4, null);
return a.removeLast();
}
""",
REMOVED,
"""
main(int value) {
var a = List.filled(value, null);
return a[value];
}
""",
KEPT,
"""
main(int value) {
var a = List.filled(1024, null);
return a[1023 & value];
}
""",
REMOVED,
"""
main(int value) {
var a = List.filled(1024, null);
return a[1024 & value];
}
""",
ABOVE_ZERO,
"""
main() {
var a = [];
return a[1];
}
""",
ABOVE_ZERO,
"""
main(int value, int Function() call) {
var a = [];
return a[value] + call() + a[value];
}
""",
ONE_ZERO_CHECK,
"""
main(bool value) {
var a = value ? [1, 2, 3] : [];
return a[1] + a[0];
}
""",
ONE_CHECK,
"""
main(int n) {
var a = List.filled(n, 1);
var sum = 0;
for (int i = 0; i <= a.length - 1; i++) {
sum += a[i];
}
return sum;
}
""",
REMOVED,
"""
main(int n) {
var a = List.filled(n, 1);
var sum = 0;
for (int i = a.length - 1; i >= 0; i--) {
sum += a[i];
}
return sum;
}
""",
REMOVED,
"""
main(dynamic value) {
value = value is int ? value as int : 42;
int sum = ~value;
for (int i = 0; i < 42; i++) sum += (value & 4);
var a = [];
if (value > a.length - 1) return;
if (value < 0) return;
return a[value];
}
""",
REMOVED,
"""
main(value) {
value = value is int ? value as int : 42;
int sum = ~value;
for (int i = 0; i < 42; i++) sum += (value & 4);
var a = [];
if (value <= a.length - 1) {
if (value >= 0) {
return a[value];
}
}
}
""",
REMOVED,
"""
main(value) {
value = value is int ? value as int : 42;
int sum = ~value;
for (int i = 0; i < 42; i++) sum += (value & 4);
var a = [];
if (value >= a.length) return;
if (value <= -1) return;
return a[value];
}
""",
REMOVED,
"""
main(int value) {
var a = List.filled(value, 1);
var sum = 0;
for (int i = 0; i < a.length; i++) {
sum += a[i];
if (sum == 0) i++;
}
return sum;
}
""",
REMOVED,
"""
main(int value) {
var a = List.filled(value, null);
var sum = 0;
for (int i = a.length - 1; i >= 0; i--) {
sum += a[i];
if (sum == 0) i--;
}
return sum;
}
""",
REMOVED,
"""
main(int value) {
var a = List.filled(6, value);
var sum = 0;
for (int i = 0; i < a.length; i++) {
sum += a[i];
if (sum == 0) i--;
}
return sum;
}
""",
REMOVED,
"""
main(int value) {
var a = List.filled(7, value);
var sum = 0;
for (int i = 0; i < a.length;) {
sum += a[i];
sum == 0 ? i-- : i++;
}
return sum;
}
""",
BELOW_ZERO_CHECK,
"""
main(int value) {
var a = List.filled(7, value);
var sum = 0;
for (int i = -2; i < a.length; i = 0) {
sum += a[i];
}
return sum;
}
""",
BELOW_ZERO_CHECK,
];
Future expect(String code, int kind) {
return compile(code, disableTypeInference: false, check: (String generated) {
switch (kind) {
case REMOVED:
Expect.isFalse(generated.contains('ioore'));
break;
case ABOVE_ZERO:
Expect.isFalse(generated.contains('< 0') || generated.contains('>= 0'));
Expect.isTrue(generated.contains('ioore'));
break;
case BELOW_ZERO_CHECK:
// May generate `!(ix < 0)` or `ix >= 0` depending if `ix` can be NaN
Expect.isTrue(generated.contains('< 0') || generated.contains('>= 0'));
Expect.isFalse(generated.contains('||') || generated.contains('&&'));
Expect.isTrue(generated.contains('ioore'));
break;
case BELOW_LENGTH:
Expect.isFalse(generated.contains('||') || generated.contains('&&'));
Expect.isTrue(generated.contains('ioore'));
break;
case KEPT:
Expect.isTrue(generated.contains('ioore'));
break;
case ONE_CHECK:
RegExp regexp = RegExp('ioore');
Iterator matches = regexp.allMatches(generated).iterator;
checkNumberOfMatches(matches, 1);
break;
case ONE_ZERO_CHECK:
RegExp regexp = RegExp('< 0|>>> 0 !==');
Iterator matches = regexp.allMatches(generated).iterator;
checkNumberOfMatches(matches, 1);
break;
}
});
}
runTests() async {
for (int i = 0; i < TESTS.length; i += 2) {
await expect(TESTS[i], TESTS[i + 1]);
}
}
main() {
asyncTest(() async {
print('--test from kernel------------------------------------------------');
await runTests();
});
}