blob: 002be4663141f15ea4a304f2951e51c8c3219c64 [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.
#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 {
#if !defined(TARGET_ARCH_IA32)
static int CountCheckBounds(FlowGraph* flow_graph) {
int checks = 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()) {
checks++;
}
}
}
return checks;
}
ISOLATE_UNIT_TEST_CASE(BoundsCheckElimination_Pragma) {
const char* kScript = R"(
import 'dart:typed_data';
@pragma('vm:unsafe:no-bounds-checks')
@pragma('vm:prefer-inline')
int foo(Uint8List list) {
int result = 0;
for (int i = 0; i < 10; i++) {
result = list[i];
}
return result;
}
int test(Uint8List list) {
return foo(list);
}
)";
const auto& root_library = Library::Handle(LoadTestScript(kScript));
const auto& function = Function::Handle(GetFunction(root_library, "test"));
TestPipeline pipeline(function, CompilerPass::kAOT);
auto flow_graph = pipeline.RunPasses({});
EXPECT_EQ(0, CountCheckBounds(flow_graph));
}
ISOLATE_UNIT_TEST_CASE(BoundsCheckElimination_Pragma_learning) {
// Test that BCE takes into account (i.e. 'learns from') checks that are
// annotated to be removed.
const char* kScript = R"(
import 'dart:typed_data';
@pragma('vm:unsafe:no-bounds-checks')
@pragma('vm:prefer-inline')
int load(Uint8List list, int index) => list[index];
int test(Uint8List list) {
int value1 = load(list, 10);
int value2 = list[5];
return value1 + value2;
}
)";
const auto& root_library = Library::Handle(LoadTestScript(kScript));
const auto& function = Function::Handle(GetFunction(root_library, "test"));
TestPipeline pipeline(function, CompilerPass::kAOT);
auto flow_graph = pipeline.RunPasses({});
// No checks because unsafe (trusted) check `list[10]` dominates `list[5]`.
EXPECT_EQ(0, CountCheckBounds(flow_graph));
}
ISOLATE_UNIT_TEST_CASE(BoundsCheckElimination_Pragma_learning_control) {
// Sister test to BoundsCheckElimination_Pragma_learning that shows without
// the annotation, there is a check that corresponds to the check removed via
// the annotation.
const char* kScript = R"(
import 'dart:typed_data';
@pragma('vm:prefer-inline')
int load(Uint8List list, int index) => list[index];
int test(Uint8List list) {
int value1 = load(list, 10);
int value2 = list[5];
return value1 + value2;
}
)";
const auto& root_library = Library::Handle(LoadTestScript(kScript));
const auto& function = Function::Handle(GetFunction(root_library, "test"));
TestPipeline pipeline(function, CompilerPass::kAOT);
auto flow_graph = pipeline.RunPasses({});
// Single check because `list[10]` dominates `list[5]`.
EXPECT_EQ(1, CountCheckBounds(flow_graph));
}
#endif // !defined(TARGET_ARCH_IA32)
} // namespace dart