blob: 9c4589e56da23ca5ef9b93ae7c57449f370e44ab [file] [log] [blame]
// Copyright (c) 2024, 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.
// Verifies that compiler can remove bounds checks when told by pragma.
import 'package:expect/expect.dart';
import 'package:vm/testing/il_matchers.dart';
import 'dart:typed_data';
@pragma('vm:never-inline')
@pragma('vm:testing:print-flow-graph')
int direct_uint8list_has_bounds_check(Uint8List list) {
int result = 0;
for (int i = 0; i < 10; i++) {
result = list[i];
}
return result;
}
@pragma('vm:unsafe:no-bounds-checks')
@pragma('vm:never-inline')
@pragma('vm:testing:print-flow-graph')
int direct_uint8list_no_bounds_check(Uint8List list) {
int result = 0;
for (int i = 0; i < 10; i++) {
result = list[i];
}
return result;
}
@pragma('vm:prefer-inline')
int helper_uint8list_no_pragma(Uint8List list) {
int result = 0;
for (int i = 0; i < 10; i++) {
result = list[i];
}
return result;
}
@pragma('vm:never-inline')
@pragma('vm:testing:print-flow-graph')
int inlined_uint8list_has_bounds_check(Uint8List list) {
if (helper_uint8list_no_pragma(list) == 42) {
return 42;
}
return 0;
}
@pragma('vm:prefer-inline')
@pragma('vm:unsafe:no-bounds-checks')
int helper_uint8list_pragma(Uint8List list) {
int result = 0;
for (int i = 0; i < 10; i++) {
result = list[i];
}
return result;
}
// For whatever reason, this passes even without the fix, despite real-world
// example in CFE (AbstractScanner.select) as well as a test in the VM
// (runtime/vm/compiler/backend/redundancy_elimination_test.cc,
// BoundsCheckElimination_Pragma_Inline) needs the fix.
@pragma('vm:never-inline')
@pragma('vm:testing:print-flow-graph')
int inlined_uint8list_no_bounds_check(Uint8List list, bool b) {
if (b) {
return helper_uint8list_pragma(list);
} else {
return helper_uint8list_pragma(list) + 42;
}
}
@pragma('vm:never-inline')
@pragma('vm:testing:print-flow-graph')
int direct_string_has_bounds_check(String s) {
int result = 0;
for (int i = 0; i < 10; i++) {
result = s.codeUnitAt(i);
}
return result;
}
@pragma('vm:unsafe:no-bounds-checks')
@pragma('vm:never-inline')
@pragma('vm:testing:print-flow-graph')
int direct_string_no_bounds_check(String s) {
int result = 0;
for (int i = 0; i < 10; i++) {
result = s.codeUnitAt(i);
}
return result;
}
@pragma('vm:prefer-inline')
int helper_string_no_pragma(String s) {
int result = 0;
for (int i = 0; i < 10; i++) {
result = s.codeUnitAt(i);
}
return result;
}
@pragma('vm:never-inline')
@pragma('vm:testing:print-flow-graph')
int inlined_string_has_bounds_check(String s) {
if (helper_string_no_pragma(s) == 42) {
return 42;
}
return 0;
}
@pragma('vm:prefer-inline')
@pragma('vm:unsafe:no-bounds-checks')
int helper_string_pragma(String s) {
int result = 0;
for (int i = 0; i < 10; i++) {
result = s.codeUnitAt(i);
}
return result;
}
@pragma('vm:never-inline')
@pragma('vm:testing:print-flow-graph')
int inlined_string_no_bounds_check(String s) {
if (helper_string_pragma(s) == 42) {
return 42;
}
return 0;
}
void main() {
// Uint8List access directly.
direct_uint8list_has_bounds_check(new Uint8List(20));
direct_uint8list_no_bounds_check(new Uint8List(20));
// Uint8List access via inlined function.
inlined_uint8list_has_bounds_check(new Uint8List(20));
inlined_uint8list_no_bounds_check(
new Uint8List(20),
new Uint8List(20).toString().length.isEven,
);
// String access directly.
direct_string_has_bounds_check(new Uint8List(20).toString());
direct_string_no_bounds_check(new Uint8List(20).toString());
// String access via inlined function.
inlined_string_has_bounds_check(new Uint8List(20).toString());
inlined_string_no_bounds_check(new Uint8List(20).toString());
}
void extractAllInstructions(dynamic data, List<String> into) {
if (data is Map) {
for (var entry in data.entries) {
if (entry.key == "o" && entry.value is String) {
into.add(entry.value);
} else {
extractAllInstructions(entry.value, into);
}
}
} else if (data is List) {
for (var entry in data) {
extractAllInstructions(entry, into);
}
} else {
if (data is int || data is String) {
// ok
} else {
print("Notice: Unhandled data: ${data.runtimeType}: $data");
}
}
}
bool hasCheckBounds(FlowGraph graph) {
List<String> ils = [];
extractAllInstructions(graph.blocks(), ils);
for (String il in ils) {
if (il == "GenericCheckBound") return true;
}
return false;
}
void matchIL$direct_uint8list_has_bounds_check(FlowGraph graph) {
Expect.isTrue(hasCheckBounds(graph), "should have bounds checks");
}
void matchIL$direct_uint8list_no_bounds_check(FlowGraph graph) {
Expect.isFalse(hasCheckBounds(graph), "should not have bounds checks");
}
void matchIL$inlined_uint8list_has_bounds_check(FlowGraph graph) {
Expect.isTrue(hasCheckBounds(graph), "should have bounds checks");
}
void matchIL$inlined_uint8list_no_bounds_check(FlowGraph graph) {
Expect.isFalse(hasCheckBounds(graph), "should not have bounds checks");
}
void matchIL$direct_string_has_bounds_check(FlowGraph graph) {
Expect.isTrue(hasCheckBounds(graph), "should have bounds checks");
}
void matchIL$direct_string_no_bounds_check(FlowGraph graph) {
Expect.isFalse(hasCheckBounds(graph), "should not have bounds checks");
}
void matchIL$inlined_string_has_bounds_check(FlowGraph graph) {
Expect.isTrue(hasCheckBounds(graph), "should have bounds checks");
}
void matchIL$inlined_string_no_bounds_check(FlowGraph graph) {
Expect.isFalse(hasCheckBounds(graph), "should not have bounds checks");
}