| // 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 |