Revert "[vm/compiler] Optimize switch statements"
This reverts commit f5228127f8570be359c2645b8c315bfafe9b0ce3.
Reason for revert: causes a VM crash, see b/242964932 (the test is also publicly available at https://github.com/simolus3/drift/blob/93fb0da38a5f33c8db289bfde696b0ca3d401b7f/drift_dev/test/analyzer/sql_queries/query_analyzer_test.dart#L8)
Original change's description:
> [vm/compiler] Optimize switch statements
>
> Switch statements that either contain only integers or only enum values of the same type can be optimized.
>
> Depending on the number of switch expressions and the number of holes that the range of switch expressions contains, either a binary search or a jump table is used.
>
> TEST=runtime/test/vm/dart{,_2}/optimized_switch
> TEST=tests/language{,_2}/switch
>
> Fixes: https://github.com/dart-lang/sdk/issues/49585
>
> Co-authored-by: Gabriel Terwesten gabriel@terwesten.net
>
> Change-Id: I62dcdb7843107f03de7e468c60b4db52ec78f676
> Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/253787
> Reviewed-by: Alexander Markov <alexmarkov@google.com>
> Commit-Queue: Alexander Markov <alexmarkov@google.com>
TBR=vegorov@google.com,alexmarkov@google.com,gabriel@terwesten.net
Change-Id: I8c673ea70e7ed91dffb3674e7dcb4aaa0611e978
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/255258
Reviewed-by: Slava Egorov <vegorov@google.com>
Reviewed-by: Ilya Yanok <yanok@google.com>
Commit-Queue: Slava Egorov <vegorov@google.com>
diff --git a/runtime/tests/vm/dart/optimized_switch_test.dart b/runtime/tests/vm/dart/optimized_switch_test.dart
deleted file mode 100644
index 9c80033..0000000
--- a/runtime/tests/vm/dart/optimized_switch_test.dart
+++ /dev/null
@@ -1,577 +0,0 @@
-// Copyright (c) 2022, 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.
-
-// Test switches that use binary search or a jump table to dispatch cases.
-
-import 'package:expect/expect.dart';
-
-void main() {
- Expect.isTrue(duplicateEnum(L._0) == 0);
- Expect.isTrue(duplicateEnum(L._1) == 1);
- Expect.isTrue(duplicateEnum(L._2) == 2);
- Expect.isTrue(duplicateEnum(L._3) == null);
-
- Expect.isTrue(duplicateInt(0) == 0);
- Expect.isTrue(duplicateInt(1) == 1);
- Expect.isTrue(duplicateInt(2) == 2);
- Expect.isTrue(duplicateInt(3) == null);
-
- Expect.isTrue(binarySearchEnumExhaustive(S._0) == 0);
- Expect.isTrue(binarySearchEnumExhaustive(S._1) == 1);
- Expect.isTrue(binarySearchEnumExhaustive(S._2) == 2);
-
- Expect.isTrue(binarySearchEnumWithDefault(null) == null);
- Expect.isTrue(binarySearchEnumWithDefault(S._0) == 0);
- Expect.isTrue(binarySearchEnumWithDefault(S._1) == null);
- Expect.isTrue(binarySearchEnumWithDefault(S._2) == 2);
-
- Expect.isTrue(binarySearchEnumHole(S._0) == 0);
- Expect.isTrue(binarySearchEnumHole(S._1) == null);
- Expect.isTrue(binarySearchEnumHole(S._2) == 2);
-
- Expect.isTrue(binarySearchEnumNoLowerBound(S._0) == null);
- Expect.isTrue(binarySearchEnumNoLowerBound(S._1) == 1);
- Expect.isTrue(binarySearchEnumNoLowerBound(S._2) == 2);
-
- Expect.isTrue(binarySearchEnumNoUpperBound(S._0) == 0);
- Expect.isTrue(binarySearchEnumNoUpperBound(S._1) == 1);
- Expect.isTrue(binarySearchEnumNoUpperBound(S._2) == null);
-
- Expect.isTrue(binarySearchInt(-2) == null);
- Expect.isTrue(binarySearchInt(-1) == -1);
- Expect.isTrue(binarySearchInt(0) == 0);
- Expect.isTrue(binarySearchInt(1) == 1);
- Expect.isTrue(binarySearchInt(2) == null);
-
- Expect.isTrue(binarySearchIntWithDefault(null) == null);
- Expect.isTrue(binarySearchIntWithDefault(-1) == null);
- Expect.isTrue(binarySearchIntWithDefault(0) == 0);
- Expect.isTrue(binarySearchIntWithDefault(1) == null);
- Expect.isTrue(binarySearchIntWithDefault(2) == 2);
- Expect.isTrue(binarySearchIntWithDefault(3) == null);
-
- Expect.isTrue(jumpTableEnumExhaustive(L._0) == 0);
- Expect.isTrue(jumpTableEnumExhaustive(L._1) == 1);
- Expect.isTrue(jumpTableEnumExhaustive(L._2) == 2);
- Expect.isTrue(jumpTableEnumExhaustive(L._3) == 3);
- Expect.isTrue(jumpTableEnumExhaustive(L._4) == 4);
- Expect.isTrue(jumpTableEnumExhaustive(L._5) == 5);
- Expect.isTrue(jumpTableEnumExhaustive(L._6) == 6);
- Expect.isTrue(jumpTableEnumExhaustive(L._7) == 7);
- Expect.isTrue(jumpTableEnumExhaustive(L._8) == 8);
- Expect.isTrue(jumpTableEnumExhaustive(L._9) == 9);
- Expect.isTrue(jumpTableEnumExhaustive(L._10) == 10);
- Expect.isTrue(jumpTableEnumExhaustive(L._11) == 11);
- Expect.isTrue(jumpTableEnumExhaustive(L._12) == 12);
- Expect.isTrue(jumpTableEnumExhaustive(L._13) == 13);
- Expect.isTrue(jumpTableEnumExhaustive(L._14) == 14);
- Expect.isTrue(jumpTableEnumExhaustive(L._15) == 15);
- Expect.isTrue(jumpTableEnumExhaustive(L._16) == 16);
-
- Expect.isTrue(jumpTableEnumWithDefault(null) == null);
- Expect.isTrue(jumpTableEnumWithDefault(L._0) == 0);
- Expect.isTrue(jumpTableEnumWithDefault(L._1) == 1);
- Expect.isTrue(jumpTableEnumWithDefault(L._2) == 2);
- Expect.isTrue(jumpTableEnumWithDefault(L._3) == 3);
- Expect.isTrue(jumpTableEnumWithDefault(L._4) == 4);
- Expect.isTrue(jumpTableEnumWithDefault(L._5) == 5);
- Expect.isTrue(jumpTableEnumWithDefault(L._6) == 6);
- Expect.isTrue(jumpTableEnumWithDefault(L._7) == 7);
- Expect.isTrue(jumpTableEnumWithDefault(L._8) == null);
- Expect.isTrue(jumpTableEnumWithDefault(L._9) == 9);
- Expect.isTrue(jumpTableEnumWithDefault(L._10) == 10);
- Expect.isTrue(jumpTableEnumWithDefault(L._11) == 11);
- Expect.isTrue(jumpTableEnumWithDefault(L._12) == 12);
- Expect.isTrue(jumpTableEnumWithDefault(L._13) == 13);
- Expect.isTrue(jumpTableEnumWithDefault(L._14) == 14);
- Expect.isTrue(jumpTableEnumWithDefault(L._15) == 15);
- Expect.isTrue(jumpTableEnumWithDefault(L._16) == 16);
-
- Expect.isTrue(jumpTableEnumHole(L._0) == 0);
- Expect.isTrue(jumpTableEnumHole(L._1) == null);
- Expect.isTrue(jumpTableEnumHole(L._2) == 2);
- Expect.isTrue(jumpTableEnumHole(L._3) == 3);
- Expect.isTrue(jumpTableEnumHole(L._4) == 4);
- Expect.isTrue(jumpTableEnumHole(L._5) == 5);
- Expect.isTrue(jumpTableEnumHole(L._6) == 6);
- Expect.isTrue(jumpTableEnumHole(L._7) == 7);
- Expect.isTrue(jumpTableEnumHole(L._8) == 8);
- Expect.isTrue(jumpTableEnumHole(L._9) == 9);
- Expect.isTrue(jumpTableEnumHole(L._10) == 10);
- Expect.isTrue(jumpTableEnumHole(L._11) == 11);
- Expect.isTrue(jumpTableEnumHole(L._12) == 12);
- Expect.isTrue(jumpTableEnumHole(L._13) == 13);
- Expect.isTrue(jumpTableEnumHole(L._14) == 14);
- Expect.isTrue(jumpTableEnumHole(L._15) == 15);
- Expect.isTrue(jumpTableEnumHole(L._16) == 16);
-
- Expect.isTrue(jumpTableEnumNoLowerBound(L._0) == null);
- Expect.isTrue(jumpTableEnumNoLowerBound(L._1) == 1);
- Expect.isTrue(jumpTableEnumNoLowerBound(L._2) == 2);
- Expect.isTrue(jumpTableEnumNoLowerBound(L._3) == 3);
- Expect.isTrue(jumpTableEnumNoLowerBound(L._4) == 4);
- Expect.isTrue(jumpTableEnumNoLowerBound(L._5) == 5);
- Expect.isTrue(jumpTableEnumNoLowerBound(L._6) == 6);
- Expect.isTrue(jumpTableEnumNoLowerBound(L._7) == 7);
- Expect.isTrue(jumpTableEnumNoLowerBound(L._8) == 8);
- Expect.isTrue(jumpTableEnumNoLowerBound(L._9) == 9);
- Expect.isTrue(jumpTableEnumNoLowerBound(L._10) == 10);
- Expect.isTrue(jumpTableEnumNoLowerBound(L._11) == 11);
- Expect.isTrue(jumpTableEnumNoLowerBound(L._12) == 12);
- Expect.isTrue(jumpTableEnumNoLowerBound(L._13) == 13);
- Expect.isTrue(jumpTableEnumNoLowerBound(L._14) == 14);
- Expect.isTrue(jumpTableEnumNoLowerBound(L._15) == 15);
- Expect.isTrue(jumpTableEnumNoLowerBound(L._16) == 16);
-
- Expect.isTrue(jumpTableEnumNoUpperBound(L._0) == 0);
- Expect.isTrue(jumpTableEnumNoUpperBound(L._1) == 1);
- Expect.isTrue(jumpTableEnumNoUpperBound(L._2) == 2);
- Expect.isTrue(jumpTableEnumNoUpperBound(L._3) == 3);
- Expect.isTrue(jumpTableEnumNoUpperBound(L._4) == 4);
- Expect.isTrue(jumpTableEnumNoUpperBound(L._5) == 5);
- Expect.isTrue(jumpTableEnumNoUpperBound(L._6) == 6);
- Expect.isTrue(jumpTableEnumNoUpperBound(L._7) == 7);
- Expect.isTrue(jumpTableEnumNoUpperBound(L._8) == 8);
- Expect.isTrue(jumpTableEnumNoUpperBound(L._9) == 9);
- Expect.isTrue(jumpTableEnumNoUpperBound(L._10) == 10);
- Expect.isTrue(jumpTableEnumNoUpperBound(L._11) == 11);
- Expect.isTrue(jumpTableEnumNoUpperBound(L._12) == 12);
- Expect.isTrue(jumpTableEnumNoUpperBound(L._13) == 13);
- Expect.isTrue(jumpTableEnumNoUpperBound(L._14) == 14);
- Expect.isTrue(jumpTableEnumNoUpperBound(L._15) == 15);
- Expect.isTrue(jumpTableEnumNoUpperBound(L._16) == null);
-
- Expect.isTrue(jumpTableInt(-2) == null);
- Expect.isTrue(jumpTableInt(-1) == -1);
- Expect.isTrue(jumpTableInt(0) == 0);
- Expect.isTrue(jumpTableInt(1) == 1);
- Expect.isTrue(jumpTableInt(2) == 2);
- Expect.isTrue(jumpTableInt(3) == 3);
- Expect.isTrue(jumpTableInt(4) == 4);
- Expect.isTrue(jumpTableInt(5) == 5);
- Expect.isTrue(jumpTableInt(6) == 6);
- Expect.isTrue(jumpTableInt(7) == 7);
- Expect.isTrue(jumpTableInt(8) == 8);
- Expect.isTrue(jumpTableInt(9) == 9);
- Expect.isTrue(jumpTableInt(10) == 10);
- Expect.isTrue(jumpTableInt(11) == 11);
- Expect.isTrue(jumpTableInt(12) == 12);
- Expect.isTrue(jumpTableInt(13) == 13);
- Expect.isTrue(jumpTableInt(14) == 14);
- Expect.isTrue(jumpTableInt(15) == null);
-
- Expect.isTrue(jumpTableIntWithDefault(null) == null);
- Expect.isTrue(jumpTableIntWithDefault(-1) == null);
- Expect.isTrue(jumpTableIntWithDefault(0) == 0);
- Expect.isTrue(jumpTableIntWithDefault(1) == 1);
- Expect.isTrue(jumpTableIntWithDefault(2) == 2);
- Expect.isTrue(jumpTableIntWithDefault(3) == 3);
- Expect.isTrue(jumpTableIntWithDefault(4) == 4);
- Expect.isTrue(jumpTableIntWithDefault(5) == 5);
- Expect.isTrue(jumpTableIntWithDefault(6) == 6);
- Expect.isTrue(jumpTableIntWithDefault(7) == 7);
- Expect.isTrue(jumpTableIntWithDefault(8) == null);
- Expect.isTrue(jumpTableIntWithDefault(9) == 9);
- Expect.isTrue(jumpTableIntWithDefault(10) == 10);
- Expect.isTrue(jumpTableIntWithDefault(11) == 11);
- Expect.isTrue(jumpTableIntWithDefault(12) == 12);
- Expect.isTrue(jumpTableIntWithDefault(13) == 13);
- Expect.isTrue(jumpTableIntWithDefault(14) == 14);
- Expect.isTrue(jumpTableIntWithDefault(15) == 15);
- Expect.isTrue(jumpTableIntWithDefault(16) == 16);
- Expect.isTrue(jumpTableIntWithDefault(17) == null);
-}
-
-/// Small enum that is used to test binary search switches.
-enum S {
- _0,
- _1,
- _2,
-}
-
-/// Large enum that is used to test jump table switches.
-///
-/// Must have enough values to trigger a jump table (currently 16) + 1 so
-/// we can create a jump table switch that is not exhaustive.
-enum L {
- _0,
- _1,
- _2,
- _3,
- _4,
- _5,
- _6,
- _7,
- _8,
- _9,
- _10,
- _11,
- _12,
- _13,
- _14,
- _15,
- _16,
-}
-
-int? duplicateEnum(L v) {
- switch (v) {
- case L._0:
- return 0;
- case L._1:
- return 1;
- case L._2:
- return 2;
- case L._1:
- return 3;
- }
-}
-
-int? duplicateInt(int v) {
- switch (v) {
- case 0:
- return 0;
- case 1:
- return 1;
- case 2:
- return 2;
- case 1:
- return 3;
- }
-}
-
-int binarySearchEnumExhaustive(S v) {
- switch (v) {
- case S._0:
- return 0;
- case S._1:
- return 1;
- case S._2:
- return 2;
- }
-}
-
-int? binarySearchEnumWithDefault(S? v) {
- switch (v) {
- case S._0:
- return 0;
- case S._2:
- return 2;
- default:
- return null;
- }
-}
-
-int? binarySearchEnumHole(S v) {
- switch (v) {
- case S._0:
- return 0;
- case S._2:
- return 2;
- }
-}
-
-int? binarySearchEnumNoLowerBound(S v) {
- switch (v) {
- case S._1:
- return 1;
- case S._2:
- return 2;
- }
-}
-
-int? binarySearchEnumNoUpperBound(S v) {
- switch (v) {
- case S._0:
- return 0;
- case S._1:
- return 1;
- }
-}
-
-int? binarySearchInt(int v) {
- switch (v) {
- case -1:
- return -1;
- case 0:
- return 0;
- case 1:
- return 1;
- }
-}
-
-int? binarySearchIntWithDefault(int? v) {
- switch (v) {
- case 0:
- return 0;
- case 2:
- return 2;
- default:
- return null;
- }
-}
-
-int jumpTableEnumExhaustive(L v) {
- switch (v) {
- case L._0:
- return 0;
- case L._1:
- return 1;
- case L._2:
- return 2;
- case L._3:
- return 3;
- case L._4:
- return 4;
- case L._5:
- return 5;
- case L._6:
- return 6;
- case L._7:
- return 7;
- case L._8:
- return 8;
- case L._9:
- return 9;
- case L._10:
- return 10;
- case L._11:
- return 11;
- case L._12:
- return 12;
- case L._13:
- return 13;
- case L._14:
- return 14;
- case L._15:
- return 15;
- case L._16:
- return 16;
- }
-}
-
-int? jumpTableEnumWithDefault(L? v) {
- switch (v) {
- case L._0:
- return 0;
- case L._1:
- return 1;
- case L._2:
- return 2;
- case L._3:
- return 3;
- case L._4:
- return 4;
- case L._5:
- return 5;
- case L._6:
- return 6;
- case L._7:
- return 7;
- case L._9:
- return 9;
- case L._10:
- return 10;
- case L._11:
- return 11;
- case L._12:
- return 12;
- case L._13:
- return 13;
- case L._14:
- return 14;
- case L._15:
- return 15;
- case L._16:
- return 16;
- default:
- return null;
- }
-}
-
-int? jumpTableEnumHole(L v) {
- switch (v) {
- case L._0:
- return 0;
- case L._2:
- return 2;
- case L._3:
- return 3;
- case L._4:
- return 4;
- case L._5:
- return 5;
- case L._6:
- return 6;
- case L._7:
- return 7;
- case L._8:
- return 8;
- case L._9:
- return 9;
- case L._10:
- return 10;
- case L._11:
- return 11;
- case L._12:
- return 12;
- case L._13:
- return 13;
- case L._14:
- return 14;
- case L._15:
- return 15;
- case L._16:
- return 16;
- }
-}
-
-int? jumpTableEnumNoLowerBound(L v) {
- switch (v) {
- case L._1:
- return 1;
- case L._2:
- return 2;
- case L._3:
- return 3;
- case L._4:
- return 4;
- case L._5:
- return 5;
- case L._6:
- return 6;
- case L._7:
- return 7;
- case L._8:
- return 8;
- case L._9:
- return 9;
- case L._10:
- return 10;
- case L._11:
- return 11;
- case L._12:
- return 12;
- case L._13:
- return 13;
- case L._14:
- return 14;
- case L._15:
- return 15;
- case L._16:
- return 16;
- }
-}
-
-int? jumpTableEnumNoUpperBound(L v) {
- switch (v) {
- case L._0:
- return 0;
- case L._1:
- return 1;
- case L._2:
- return 2;
- case L._3:
- return 3;
- case L._4:
- return 4;
- case L._5:
- return 5;
- case L._6:
- return 6;
- case L._7:
- return 7;
- case L._8:
- return 8;
- case L._9:
- return 9;
- case L._10:
- return 10;
- case L._11:
- return 11;
- case L._12:
- return 12;
- case L._13:
- return 13;
- case L._14:
- return 14;
- case L._15:
- return 15;
- }
-}
-
-int? jumpTableInt(int v) {
- switch (v) {
- case -1:
- return -1;
- case 0:
- return 0;
- case 1:
- return 1;
- case 2:
- return 2;
- case 3:
- return 3;
- case 4:
- return 4;
- case 5:
- return 5;
- case 6:
- return 6;
- case 7:
- return 7;
- case 8:
- return 8;
- case 9:
- return 9;
- case 10:
- return 10;
- case 11:
- return 11;
- case 12:
- return 12;
- case 13:
- return 13;
- case 14:
- return 14;
- }
-}
-
-int? jumpTableIntWithDefault(int? v) {
- switch (v) {
- case 0:
- return 0;
- case 1:
- return 1;
- case 2:
- return 2;
- case 3:
- return 3;
- case 4:
- return 4;
- case 5:
- return 5;
- case 6:
- return 6;
- case 7:
- return 7;
- case 9:
- return 9;
- case 10:
- return 10;
- case 11:
- return 11;
- case 12:
- return 12;
- case 13:
- return 13;
- case 14:
- return 14;
- case 15:
- return 15;
- case 16:
- return 16;
- default:
- return null;
- }
-}
diff --git a/runtime/tests/vm/dart_2/optimized_switch_test.dart b/runtime/tests/vm/dart_2/optimized_switch_test.dart
deleted file mode 100644
index ed63635..0000000
--- a/runtime/tests/vm/dart_2/optimized_switch_test.dart
+++ /dev/null
@@ -1,579 +0,0 @@
-// Copyright (c) 2022, 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.
-
-// @dart = 2.9
-
-// Test switches that use binary search or a jump table to dispatch cases.
-
-import 'package:expect/expect.dart';
-
-void main() {
- Expect.isTrue(duplicateEnum(L._0) == 0);
- Expect.isTrue(duplicateEnum(L._1) == 1);
- Expect.isTrue(duplicateEnum(L._2) == 2);
- Expect.isTrue(duplicateEnum(L._3) == null);
-
- Expect.isTrue(duplicateInt(0) == 0);
- Expect.isTrue(duplicateInt(1) == 1);
- Expect.isTrue(duplicateInt(2) == 2);
- Expect.isTrue(duplicateInt(3) == null);
-
- Expect.isTrue(binarySearchEnumExhaustive(S._0) == 0);
- Expect.isTrue(binarySearchEnumExhaustive(S._1) == 1);
- Expect.isTrue(binarySearchEnumExhaustive(S._2) == 2);
-
- Expect.isTrue(binarySearchEnumWithDefault(null) == null);
- Expect.isTrue(binarySearchEnumWithDefault(S._0) == 0);
- Expect.isTrue(binarySearchEnumWithDefault(S._1) == null);
- Expect.isTrue(binarySearchEnumWithDefault(S._2) == 2);
-
- Expect.isTrue(binarySearchEnumHole(S._0) == 0);
- Expect.isTrue(binarySearchEnumHole(S._1) == null);
- Expect.isTrue(binarySearchEnumHole(S._2) == 2);
-
- Expect.isTrue(binarySearchEnumNoLowerBound(S._0) == null);
- Expect.isTrue(binarySearchEnumNoLowerBound(S._1) == 1);
- Expect.isTrue(binarySearchEnumNoLowerBound(S._2) == 2);
-
- Expect.isTrue(binarySearchEnumNoUpperBound(S._0) == 0);
- Expect.isTrue(binarySearchEnumNoUpperBound(S._1) == 1);
- Expect.isTrue(binarySearchEnumNoUpperBound(S._2) == null);
-
- Expect.isTrue(binarySearchInt(-2) == null);
- Expect.isTrue(binarySearchInt(-1) == -1);
- Expect.isTrue(binarySearchInt(0) == 0);
- Expect.isTrue(binarySearchInt(1) == 1);
- Expect.isTrue(binarySearchInt(2) == null);
-
- Expect.isTrue(binarySearchIntWithDefault(null) == null);
- Expect.isTrue(binarySearchIntWithDefault(-1) == null);
- Expect.isTrue(binarySearchIntWithDefault(0) == 0);
- Expect.isTrue(binarySearchIntWithDefault(1) == null);
- Expect.isTrue(binarySearchIntWithDefault(2) == 2);
- Expect.isTrue(binarySearchIntWithDefault(3) == null);
-
- Expect.isTrue(jumpTableEnumExhaustive(L._0) == 0);
- Expect.isTrue(jumpTableEnumExhaustive(L._1) == 1);
- Expect.isTrue(jumpTableEnumExhaustive(L._2) == 2);
- Expect.isTrue(jumpTableEnumExhaustive(L._3) == 3);
- Expect.isTrue(jumpTableEnumExhaustive(L._4) == 4);
- Expect.isTrue(jumpTableEnumExhaustive(L._5) == 5);
- Expect.isTrue(jumpTableEnumExhaustive(L._6) == 6);
- Expect.isTrue(jumpTableEnumExhaustive(L._7) == 7);
- Expect.isTrue(jumpTableEnumExhaustive(L._8) == 8);
- Expect.isTrue(jumpTableEnumExhaustive(L._9) == 9);
- Expect.isTrue(jumpTableEnumExhaustive(L._10) == 10);
- Expect.isTrue(jumpTableEnumExhaustive(L._11) == 11);
- Expect.isTrue(jumpTableEnumExhaustive(L._12) == 12);
- Expect.isTrue(jumpTableEnumExhaustive(L._13) == 13);
- Expect.isTrue(jumpTableEnumExhaustive(L._14) == 14);
- Expect.isTrue(jumpTableEnumExhaustive(L._15) == 15);
- Expect.isTrue(jumpTableEnumExhaustive(L._16) == 16);
-
- Expect.isTrue(jumpTableEnumWithDefault(null) == null);
- Expect.isTrue(jumpTableEnumWithDefault(L._0) == 0);
- Expect.isTrue(jumpTableEnumWithDefault(L._1) == 1);
- Expect.isTrue(jumpTableEnumWithDefault(L._2) == 2);
- Expect.isTrue(jumpTableEnumWithDefault(L._3) == 3);
- Expect.isTrue(jumpTableEnumWithDefault(L._4) == 4);
- Expect.isTrue(jumpTableEnumWithDefault(L._5) == 5);
- Expect.isTrue(jumpTableEnumWithDefault(L._6) == 6);
- Expect.isTrue(jumpTableEnumWithDefault(L._7) == 7);
- Expect.isTrue(jumpTableEnumWithDefault(L._8) == null);
- Expect.isTrue(jumpTableEnumWithDefault(L._9) == 9);
- Expect.isTrue(jumpTableEnumWithDefault(L._10) == 10);
- Expect.isTrue(jumpTableEnumWithDefault(L._11) == 11);
- Expect.isTrue(jumpTableEnumWithDefault(L._12) == 12);
- Expect.isTrue(jumpTableEnumWithDefault(L._13) == 13);
- Expect.isTrue(jumpTableEnumWithDefault(L._14) == 14);
- Expect.isTrue(jumpTableEnumWithDefault(L._15) == 15);
- Expect.isTrue(jumpTableEnumWithDefault(L._16) == 16);
-
- Expect.isTrue(jumpTableEnumHole(L._0) == 0);
- Expect.isTrue(jumpTableEnumHole(L._1) == null);
- Expect.isTrue(jumpTableEnumHole(L._2) == 2);
- Expect.isTrue(jumpTableEnumHole(L._3) == 3);
- Expect.isTrue(jumpTableEnumHole(L._4) == 4);
- Expect.isTrue(jumpTableEnumHole(L._5) == 5);
- Expect.isTrue(jumpTableEnumHole(L._6) == 6);
- Expect.isTrue(jumpTableEnumHole(L._7) == 7);
- Expect.isTrue(jumpTableEnumHole(L._8) == 8);
- Expect.isTrue(jumpTableEnumHole(L._9) == 9);
- Expect.isTrue(jumpTableEnumHole(L._10) == 10);
- Expect.isTrue(jumpTableEnumHole(L._11) == 11);
- Expect.isTrue(jumpTableEnumHole(L._12) == 12);
- Expect.isTrue(jumpTableEnumHole(L._13) == 13);
- Expect.isTrue(jumpTableEnumHole(L._14) == 14);
- Expect.isTrue(jumpTableEnumHole(L._15) == 15);
- Expect.isTrue(jumpTableEnumHole(L._16) == 16);
-
- Expect.isTrue(jumpTableEnumNoLowerBound(L._0) == null);
- Expect.isTrue(jumpTableEnumNoLowerBound(L._1) == 1);
- Expect.isTrue(jumpTableEnumNoLowerBound(L._2) == 2);
- Expect.isTrue(jumpTableEnumNoLowerBound(L._3) == 3);
- Expect.isTrue(jumpTableEnumNoLowerBound(L._4) == 4);
- Expect.isTrue(jumpTableEnumNoLowerBound(L._5) == 5);
- Expect.isTrue(jumpTableEnumNoLowerBound(L._6) == 6);
- Expect.isTrue(jumpTableEnumNoLowerBound(L._7) == 7);
- Expect.isTrue(jumpTableEnumNoLowerBound(L._8) == 8);
- Expect.isTrue(jumpTableEnumNoLowerBound(L._9) == 9);
- Expect.isTrue(jumpTableEnumNoLowerBound(L._10) == 10);
- Expect.isTrue(jumpTableEnumNoLowerBound(L._11) == 11);
- Expect.isTrue(jumpTableEnumNoLowerBound(L._12) == 12);
- Expect.isTrue(jumpTableEnumNoLowerBound(L._13) == 13);
- Expect.isTrue(jumpTableEnumNoLowerBound(L._14) == 14);
- Expect.isTrue(jumpTableEnumNoLowerBound(L._15) == 15);
- Expect.isTrue(jumpTableEnumNoLowerBound(L._16) == 16);
-
- Expect.isTrue(jumpTableEnumNoUpperBound(L._0) == 0);
- Expect.isTrue(jumpTableEnumNoUpperBound(L._1) == 1);
- Expect.isTrue(jumpTableEnumNoUpperBound(L._2) == 2);
- Expect.isTrue(jumpTableEnumNoUpperBound(L._3) == 3);
- Expect.isTrue(jumpTableEnumNoUpperBound(L._4) == 4);
- Expect.isTrue(jumpTableEnumNoUpperBound(L._5) == 5);
- Expect.isTrue(jumpTableEnumNoUpperBound(L._6) == 6);
- Expect.isTrue(jumpTableEnumNoUpperBound(L._7) == 7);
- Expect.isTrue(jumpTableEnumNoUpperBound(L._8) == 8);
- Expect.isTrue(jumpTableEnumNoUpperBound(L._9) == 9);
- Expect.isTrue(jumpTableEnumNoUpperBound(L._10) == 10);
- Expect.isTrue(jumpTableEnumNoUpperBound(L._11) == 11);
- Expect.isTrue(jumpTableEnumNoUpperBound(L._12) == 12);
- Expect.isTrue(jumpTableEnumNoUpperBound(L._13) == 13);
- Expect.isTrue(jumpTableEnumNoUpperBound(L._14) == 14);
- Expect.isTrue(jumpTableEnumNoUpperBound(L._15) == 15);
- Expect.isTrue(jumpTableEnumNoUpperBound(L._16) == null);
-
- Expect.isTrue(jumpTableInt(-2) == null);
- Expect.isTrue(jumpTableInt(-1) == -1);
- Expect.isTrue(jumpTableInt(0) == 0);
- Expect.isTrue(jumpTableInt(1) == 1);
- Expect.isTrue(jumpTableInt(2) == 2);
- Expect.isTrue(jumpTableInt(3) == 3);
- Expect.isTrue(jumpTableInt(4) == 4);
- Expect.isTrue(jumpTableInt(5) == 5);
- Expect.isTrue(jumpTableInt(6) == 6);
- Expect.isTrue(jumpTableInt(7) == 7);
- Expect.isTrue(jumpTableInt(8) == 8);
- Expect.isTrue(jumpTableInt(9) == 9);
- Expect.isTrue(jumpTableInt(10) == 10);
- Expect.isTrue(jumpTableInt(11) == 11);
- Expect.isTrue(jumpTableInt(12) == 12);
- Expect.isTrue(jumpTableInt(13) == 13);
- Expect.isTrue(jumpTableInt(14) == 14);
- Expect.isTrue(jumpTableInt(15) == null);
-
- Expect.isTrue(jumpTableIntWithDefault(null) == null);
- Expect.isTrue(jumpTableIntWithDefault(-1) == null);
- Expect.isTrue(jumpTableIntWithDefault(0) == 0);
- Expect.isTrue(jumpTableIntWithDefault(1) == 1);
- Expect.isTrue(jumpTableIntWithDefault(2) == 2);
- Expect.isTrue(jumpTableIntWithDefault(3) == 3);
- Expect.isTrue(jumpTableIntWithDefault(4) == 4);
- Expect.isTrue(jumpTableIntWithDefault(5) == 5);
- Expect.isTrue(jumpTableIntWithDefault(6) == 6);
- Expect.isTrue(jumpTableIntWithDefault(7) == 7);
- Expect.isTrue(jumpTableIntWithDefault(8) == null);
- Expect.isTrue(jumpTableIntWithDefault(9) == 9);
- Expect.isTrue(jumpTableIntWithDefault(10) == 10);
- Expect.isTrue(jumpTableIntWithDefault(11) == 11);
- Expect.isTrue(jumpTableIntWithDefault(12) == 12);
- Expect.isTrue(jumpTableIntWithDefault(13) == 13);
- Expect.isTrue(jumpTableIntWithDefault(14) == 14);
- Expect.isTrue(jumpTableIntWithDefault(15) == 15);
- Expect.isTrue(jumpTableIntWithDefault(16) == 16);
- Expect.isTrue(jumpTableIntWithDefault(17) == null);
-}
-
-/// Small enum that is used to test binary search switches.
-enum S {
- _0,
- _1,
- _2,
-}
-
-/// Large enum that is used to test jump table switches.
-///
-/// Must have enough values to trigger a jump table (currently 16) + 1 so
-/// we can create a jump table switch that is not exhaustive.
-enum L {
- _0,
- _1,
- _2,
- _3,
- _4,
- _5,
- _6,
- _7,
- _8,
- _9,
- _10,
- _11,
- _12,
- _13,
- _14,
- _15,
- _16,
-}
-
-int duplicateEnum(L v) {
- switch (v) {
- case L._0:
- return 0;
- case L._1:
- return 1;
- case L._2:
- return 2;
- case L._1:
- return 3;
- }
-}
-
-int duplicateInt(int v) {
- switch (v) {
- case 0:
- return 0;
- case 1:
- return 1;
- case 2:
- return 2;
- case 1:
- return 3;
- }
-}
-
-int binarySearchEnumExhaustive(S v) {
- switch (v) {
- case S._0:
- return 0;
- case S._1:
- return 1;
- case S._2:
- return 2;
- }
-}
-
-int binarySearchEnumWithDefault(S v) {
- switch (v) {
- case S._0:
- return 0;
- case S._2:
- return 2;
- default:
- return null;
- }
-}
-
-int binarySearchEnumHole(S v) {
- switch (v) {
- case S._0:
- return 0;
- case S._2:
- return 2;
- }
-}
-
-int binarySearchEnumNoLowerBound(S v) {
- switch (v) {
- case S._1:
- return 1;
- case S._2:
- return 2;
- }
-}
-
-int binarySearchEnumNoUpperBound(S v) {
- switch (v) {
- case S._0:
- return 0;
- case S._1:
- return 1;
- }
-}
-
-int binarySearchInt(int v) {
- switch (v) {
- case -1:
- return -1;
- case 0:
- return 0;
- case 1:
- return 1;
- }
-}
-
-int binarySearchIntWithDefault(int v) {
- switch (v) {
- case 0:
- return 0;
- case 2:
- return 2;
- default:
- return null;
- }
-}
-
-int jumpTableEnumExhaustive(L v) {
- switch (v) {
- case L._0:
- return 0;
- case L._1:
- return 1;
- case L._2:
- return 2;
- case L._3:
- return 3;
- case L._4:
- return 4;
- case L._5:
- return 5;
- case L._6:
- return 6;
- case L._7:
- return 7;
- case L._8:
- return 8;
- case L._9:
- return 9;
- case L._10:
- return 10;
- case L._11:
- return 11;
- case L._12:
- return 12;
- case L._13:
- return 13;
- case L._14:
- return 14;
- case L._15:
- return 15;
- case L._16:
- return 16;
- }
-}
-
-int jumpTableEnumWithDefault(L v) {
- switch (v) {
- case L._0:
- return 0;
- case L._1:
- return 1;
- case L._2:
- return 2;
- case L._3:
- return 3;
- case L._4:
- return 4;
- case L._5:
- return 5;
- case L._6:
- return 6;
- case L._7:
- return 7;
- case L._9:
- return 9;
- case L._10:
- return 10;
- case L._11:
- return 11;
- case L._12:
- return 12;
- case L._13:
- return 13;
- case L._14:
- return 14;
- case L._15:
- return 15;
- case L._16:
- return 16;
- default:
- return null;
- }
-}
-
-int jumpTableEnumHole(L v) {
- switch (v) {
- case L._0:
- return 0;
- case L._2:
- return 2;
- case L._3:
- return 3;
- case L._4:
- return 4;
- case L._5:
- return 5;
- case L._6:
- return 6;
- case L._7:
- return 7;
- case L._8:
- return 8;
- case L._9:
- return 9;
- case L._10:
- return 10;
- case L._11:
- return 11;
- case L._12:
- return 12;
- case L._13:
- return 13;
- case L._14:
- return 14;
- case L._15:
- return 15;
- case L._16:
- return 16;
- }
-}
-
-int jumpTableEnumNoLowerBound(L v) {
- switch (v) {
- case L._1:
- return 1;
- case L._2:
- return 2;
- case L._3:
- return 3;
- case L._4:
- return 4;
- case L._5:
- return 5;
- case L._6:
- return 6;
- case L._7:
- return 7;
- case L._8:
- return 8;
- case L._9:
- return 9;
- case L._10:
- return 10;
- case L._11:
- return 11;
- case L._12:
- return 12;
- case L._13:
- return 13;
- case L._14:
- return 14;
- case L._15:
- return 15;
- case L._16:
- return 16;
- }
-}
-
-int jumpTableEnumNoUpperBound(L v) {
- switch (v) {
- case L._0:
- return 0;
- case L._1:
- return 1;
- case L._2:
- return 2;
- case L._3:
- return 3;
- case L._4:
- return 4;
- case L._5:
- return 5;
- case L._6:
- return 6;
- case L._7:
- return 7;
- case L._8:
- return 8;
- case L._9:
- return 9;
- case L._10:
- return 10;
- case L._11:
- return 11;
- case L._12:
- return 12;
- case L._13:
- return 13;
- case L._14:
- return 14;
- case L._15:
- return 15;
- }
-}
-
-int jumpTableInt(int v) {
- switch (v) {
- case -1:
- return -1;
- case 0:
- return 0;
- case 1:
- return 1;
- case 2:
- return 2;
- case 3:
- return 3;
- case 4:
- return 4;
- case 5:
- return 5;
- case 6:
- return 6;
- case 7:
- return 7;
- case 8:
- return 8;
- case 9:
- return 9;
- case 10:
- return 10;
- case 11:
- return 11;
- case 12:
- return 12;
- case 13:
- return 13;
- case 14:
- return 14;
- }
-}
-
-int jumpTableIntWithDefault(int v) {
- switch (v) {
- case 0:
- return 0;
- case 1:
- return 1;
- case 2:
- return 2;
- case 3:
- return 3;
- case 4:
- return 4;
- case 5:
- return 5;
- case 6:
- return 6;
- case 7:
- return 7;
- case 9:
- return 9;
- case 10:
- return 10;
- case 11:
- return 11;
- case 12:
- return 12;
- case 13:
- return 13;
- case 14:
- return 14;
- case 15:
- return 15;
- case 16:
- return 16;
- default:
- return null;
- }
-}
diff --git a/runtime/vm/compiler/backend/branch_optimizer.cc b/runtime/vm/compiler/backend/branch_optimizer.cc
index 23511b4..3c58635 100644
--- a/runtime/vm/compiler/backend/branch_optimizer.cc
+++ b/runtime/vm/compiler/backend/branch_optimizer.cc
@@ -284,10 +284,8 @@
BranchInstr* branch = pred->last_instruction()->AsBranch();
if (branch == nullptr) {
- // There is no "B_pred" block, or the block is the IndirectGoto
- // of a switch that uses it as a jump table.
- ASSERT(pred->last_instruction()->IsGraphEntry() ||
- pred->last_instruction()->IsIndirectGoto());
+ // There is no "B_pred" block.
+ ASSERT(pred->last_instruction()->IsGraphEntry());
continue;
}
diff --git a/runtime/vm/compiler/backend/constant_propagator.cc b/runtime/vm/compiler/backend/constant_propagator.cc
index b2c9ad3..b021b2e 100644
--- a/runtime/vm/compiler/backend/constant_propagator.cc
+++ b/runtime/vm/compiler/backend/constant_propagator.cc
@@ -232,10 +232,8 @@
}
void ConstantPropagator::VisitIndirectGoto(IndirectGotoInstr* instr) {
- if (reachable_->Contains(instr->GetBlock()->preorder_number())) {
- for (intptr_t i = 0; i < instr->SuccessorCount(); i++) {
- SetReachable(instr->SuccessorAt(i));
- }
+ for (intptr_t i = 0; i < instr->SuccessorCount(); i++) {
+ SetReachable(instr->SuccessorAt(i));
}
}
@@ -1504,11 +1502,7 @@
}
static bool IsEmptyBlock(BlockEntryInstr* block) {
- // A block containing a goto to itself forms an infinite loop.
- // We don't consider this an empty block to handle the edge-case where code
- // reduces to an infinite loop.
- return block->next()->IsGoto() &&
- block->next()->AsGoto()->successor() != block && !HasPhis(block) &&
+ return block->next()->IsGoto() && !HasPhis(block) &&
!block->IsIndirectEntry();
}
diff --git a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
index 6c5b044..921a465 100644
--- a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
+++ b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
@@ -1472,8 +1472,7 @@
return flow_graph_builder_->LoadLocal(variable);
}
-IndirectGotoInstr* StreamingFlowGraphBuilder::IndirectGoto(
- intptr_t target_count) {
+Fragment StreamingFlowGraphBuilder::IndirectGoto(intptr_t target_count) {
return flow_graph_builder_->IndirectGoto(target_count);
}
@@ -3348,8 +3347,8 @@
instructions += BuildArguments(
&argument_names, &argument_count,
/* positional_argument_count = */ nullptr); // read arguments.
- ++argument_count; // include receiver
- SkipInterfaceMemberNameReference(); // interfaceTargetReference
+ ++argument_count; // include receiver
+ SkipInterfaceMemberNameReference(); // interfaceTargetReference
return instructions +
StaticCall(position, Function::ZoneHandle(Z, function.ptr()),
argument_count, argument_names, ICData::kSuper,
@@ -4277,7 +4276,7 @@
const TokenPosition pos = ReadPosition(); // read file offset.
if (position != nullptr) *position = pos;
- ReadPosition(); // read file end offset.
+ ReadPosition(); // read file end offset.
intptr_t list_length = ReadListLength(); // read number of statements.
for (intptr_t i = 0; i < list_length; ++i) {
@@ -4415,7 +4414,7 @@
const TokenPosition pos = ReadPosition(); // read position.
if (position != nullptr) *position = pos;
- intptr_t target_index = ReadUInt(); // read target index.
+ intptr_t target_index = ReadUInt(); // read target index.
TryFinallyBlock* outer_finally = nullptr;
intptr_t target_context_depth = -1;
@@ -4442,7 +4441,7 @@
const TokenPosition pos = ReadPosition(); // read position.
if (position != nullptr) *position = pos;
- TestFragment condition = TranslateConditionForControl(); // read condition.
+ TestFragment condition = TranslateConditionForControl(); // read condition.
const Fragment body = BuildStatementWithBranchCoverage(); // read body
Fragment body_entry(condition.CreateTrueSuccessor(flow_graph_builder_));
@@ -4583,7 +4582,7 @@
const TokenPosition pos = ReadPosition(); // read position.
if (position != nullptr) *position = pos;
- TokenPosition body_position = ReadPosition(); // read body position.
+ TokenPosition body_position = ReadPosition(); // read body position.
intptr_t variable_kernel_position = ReaderOffset() + data_program_offset_;
SkipVariableDeclaration(); // read variable.
@@ -4641,186 +4640,150 @@
TokenPosition* position) {
const TokenPosition pos = ReadPosition(); // read position.
if (position != nullptr) *position = pos;
- const bool is_exhaustive = ReadBool(); // read exhaustive flag.
+ ReadBool(); // read exhaustive flag.
// We need the number of cases. So start by getting that, then go back.
- const intptr_t offset = ReaderOffset();
- SkipExpression(); // temporarily skip condition
- intptr_t case_count = ReadListLength(); // read number of cases.
+ intptr_t offset = ReaderOffset();
+ SkipExpression(); // temporarily skip condition
+ int case_count = ReadListLength(); // read number of cases.
SetOffset(offset);
SwitchBlock block(flow_graph_builder_, case_count);
- Fragment instructions = BuildExpression(); // read condition.
- instructions +=
+ // Instead of using a variable we should reuse the expression on the stack,
+ // since it won't be assigned again, we don't need phi nodes.
+ Fragment head_instructions = BuildExpression(); // read condition.
+ head_instructions +=
StoreLocal(TokenPosition::kNoSource, scopes()->switch_variable);
- instructions += Drop();
+ head_instructions += Drop();
case_count = ReadListLength(); // read number of cases.
- SwitchHelper helper(Z, pos, is_exhaustive, &block, case_count);
-
- // Build the case bodies and collect the expressions into the helper
- // for the next step.
- for (intptr_t i = 0; i < case_count; ++i) {
- helper.AddCaseBody(BuildSwitchCase(&helper, i));
- }
-
- // Build the code to dispatch to the case bodies.
- switch (helper.SelectDispatchStrategy()) {
- case kSwitchDispatchAuto:
- UNREACHABLE();
- case kSwitchDispatchLinearScan:
- instructions += BuildLinearScanSwitch(&helper);
- break;
- case kSwitchDispatchBinarySearch:
- instructions += BuildBinarySearchSwitch(&helper);
- break;
- case kSwitchDispatchJumpTable:
- instructions += BuildJumpTableSwitch(&helper);
- break;
- }
-
- return instructions;
-}
-
-Fragment StreamingFlowGraphBuilder::BuildSwitchCase(SwitchHelper* helper,
- intptr_t case_index) {
- // Generate case body and try to find out whether the body will be target
+ // Phase 1: Generate bodies and try to find out whether a body will be target
// of a jump due to:
// * `continue case_label`
// * `case e1: case e2: body`
- //
- // Also collect switch expressions into helper.
-
- const int expression_count = ReadListLength(); // read number of expressions.
- for (intptr_t j = 0; j < expression_count; ++j) {
- const TokenPosition pos = ReadPosition(); // read jth position.
- // read jth expression.
- const Instance& value =
- Instance::ZoneHandle(Z, constant_reader_.ReadConstantExpression());
- helper->AddExpression(case_index, pos, value);
- }
-
- const bool is_default = ReadBool(); // read is_default.
- if (is_default) helper->set_default_case(case_index);
- Fragment body_fragment = BuildStatementWithBranchCoverage(); // read body.
-
- if (body_fragment.entry == nullptr) {
- // Make a NOP in order to ensure linking works properly.
- body_fragment = NullConstant();
- body_fragment += Drop();
- }
-
- // The Dart language specification mandates fall-throughs in [SwitchCase]es
- // to be runtime errors.
- if (!is_default && body_fragment.is_open() &&
- (case_index < (helper->case_count() - 1))) {
- const Class& klass = Class::ZoneHandle(
- Z, Library::LookupCoreClass(Symbols::FallThroughError()));
- ASSERT(!klass.IsNull());
- const auto& error = klass.EnsureIsFinalized(thread());
- ASSERT(error == Error::null());
-
- GrowableHandlePtrArray<const String> pieces(Z, 3);
- pieces.Add(Symbols::FallThroughError());
- pieces.Add(Symbols::Dot());
- pieces.Add(H.DartSymbolObfuscate("_create"));
-
- const Function& constructor = Function::ZoneHandle(
- Z, klass.LookupConstructorAllowPrivate(String::ZoneHandle(
- Z, Symbols::FromConcatAll(H.thread(), pieces))));
- ASSERT(!constructor.IsNull());
- const String& url = H.DartSymbolPlain(
- parsed_function()->function().ToLibNamePrefixedQualifiedCString());
-
- // Create instance of _FallThroughError
- body_fragment += AllocateObject(TokenPosition::kNoSource, klass, 0);
- LocalVariable* instance = MakeTemporary();
-
- // Call _FallThroughError._create constructor.
- body_fragment += LoadLocal(instance); // this
- body_fragment += Constant(url); // url
- body_fragment += NullConstant(); // line
-
- body_fragment +=
- StaticCall(TokenPosition::kNoSource, constructor, 3, ICData::kStatic);
- body_fragment += Drop();
-
- // Throw the exception
- body_fragment += ThrowException(TokenPosition::kNoSource);
- body_fragment += Drop();
- }
-
- // If there is an implicit fall-through we have one [SwitchCase] and
- // multiple expressions, e.g.
- //
- // switch(expr) {
- // case a:
- // case b:
- // <stmt-body>
- // }
- //
- // This means that the <stmt-body> will have more than 1 incoming edge (one
- // from `a == expr` and one from `a != expr && b == expr`). The
- // `block.Destination()` records the additional jump.
- if (expression_count > 1) {
- helper->switch_block()->DestinationDirect(case_index);
- }
-
- return body_fragment;
-}
-
-Fragment StreamingFlowGraphBuilder::BuildLinearScanSwitch(
- SwitchHelper* helper) {
- // Build a switch using a sequence of equality tests.
- //
- // From a test:
- // * jump directly to a body, if there is no jumper.
- // * jump to a wrapper block which jumps to the body, if there is a jumper.
-
- SwitchBlock* block = helper->switch_block();
- const intptr_t case_count = helper->case_count();
- const intptr_t default_case = helper->default_case();
- const GrowableArray<Fragment>& case_bodies = helper->case_bodies();
- Fragment current_instructions;
- intptr_t expression_index = 0;
+ Fragment* body_fragments = Z->Alloc<Fragment>(case_count);
+ intptr_t* case_expression_offsets = Z->Alloc<intptr_t>(case_count);
+ int default_case = -1;
for (intptr_t i = 0; i < case_count; ++i) {
+ case_expression_offsets[i] = ReaderOffset();
+ int expression_count = ReadListLength(); // read number of expressions.
+ for (intptr_t j = 0; j < expression_count; ++j) {
+ ReadPosition(); // read jth position.
+ SkipExpression(); // read jth expression.
+ }
+ bool is_default = ReadBool(); // read is_default.
+ if (is_default) default_case = i;
+ Fragment& body_fragment = body_fragments[i] =
+ BuildStatementWithBranchCoverage(); // read body.
+
+ if (body_fragment.entry == nullptr) {
+ // Make a NOP in order to ensure linking works properly.
+ body_fragment = NullConstant();
+ body_fragment += Drop();
+ }
+
+ // The Dart language specification mandates fall-throughs in [SwitchCase]es
+ // to be runtime errors.
+ if (!is_default && body_fragment.is_open() && (i < (case_count - 1))) {
+ const Class& klass = Class::ZoneHandle(
+ Z, Library::LookupCoreClass(Symbols::FallThroughError()));
+ ASSERT(!klass.IsNull());
+ const auto& error = klass.EnsureIsFinalized(thread());
+ ASSERT(error == Error::null());
+
+ GrowableHandlePtrArray<const String> pieces(Z, 3);
+ pieces.Add(Symbols::FallThroughError());
+ pieces.Add(Symbols::Dot());
+ pieces.Add(H.DartSymbolObfuscate("_create"));
+
+ const Function& constructor = Function::ZoneHandle(
+ Z, klass.LookupConstructorAllowPrivate(String::ZoneHandle(
+ Z, Symbols::FromConcatAll(H.thread(), pieces))));
+ ASSERT(!constructor.IsNull());
+ const String& url = H.DartSymbolPlain(
+ parsed_function()->function().ToLibNamePrefixedQualifiedCString());
+
+ // Create instance of _FallThroughError
+ body_fragment += AllocateObject(TokenPosition::kNoSource, klass, 0);
+ LocalVariable* instance = MakeTemporary();
+
+ // Call _FallThroughError._create constructor.
+ body_fragment += LoadLocal(instance); // this
+ body_fragment += Constant(url); // url
+ body_fragment += NullConstant(); // line
+
+ body_fragment +=
+ StaticCall(TokenPosition::kNoSource, constructor, 3, ICData::kStatic);
+ body_fragment += Drop();
+
+ // Throw the exception
+ body_fragment += ThrowException(TokenPosition::kNoSource);
+ body_fragment += Drop();
+ }
+
+ // If there is an implicit fall-through we have one [SwitchCase] and
+ // multiple expressions, e.g.
+ //
+ // switch(expr) {
+ // case a:
+ // case b:
+ // <stmt-body>
+ // }
+ //
+ // This means that the <stmt-body> will have more than 1 incoming edge (one
+ // from `a == expr` and one from `a != expr && b == expr`). The
+ // `block.Destination()` records the additional jump.
+ if (expression_count > 1) {
+ block.DestinationDirect(i);
+ }
+ }
+
+ intptr_t end_offset = ReaderOffset();
+
+ // Phase 2: Generate everything except the real bodies:
+ // * jump directly to a body (if there is no jumper)
+ // * jump to a wrapper block which jumps to the body (if there is a jumper)
+ Fragment current_instructions = head_instructions;
+ for (intptr_t i = 0; i < case_count; ++i) {
+ SetOffset(case_expression_offsets[i]);
+ int expression_count = ReadListLength(); // read length of expressions.
+
if (i == default_case) {
ASSERT(i == (case_count - 1));
- if (block->HadJumper(i)) {
+ if (block.HadJumper(i)) {
// There are several branches to the body, so we will make a goto to
// the join block (and prepend a join instruction to the real body).
- JoinEntryInstr* join = block->DestinationDirect(i);
+ JoinEntryInstr* join = block.DestinationDirect(i);
current_instructions += Goto(join);
current_instructions = Fragment(current_instructions.entry, join);
- current_instructions += case_bodies[i];
+ current_instructions += body_fragments[i];
} else {
- current_instructions += case_bodies[i];
+ current_instructions += body_fragments[i];
}
} else {
JoinEntryInstr* body_join = nullptr;
- if (block->HadJumper(i)) {
- body_join = block->DestinationDirect(i);
- case_bodies[i] = Fragment(body_join) + case_bodies[i];
+ if (block.HadJumper(i)) {
+ body_join = block.DestinationDirect(i);
+ body_fragments[i] = Fragment(body_join) + body_fragments[i];
}
- const intptr_t expression_count = helper->case_expression_counts().At(i);
for (intptr_t j = 0; j < expression_count; ++j) {
TargetEntryInstr* then;
TargetEntryInstr* otherwise;
- const SwitchExpression& expression =
- helper->expressions().At(expression_index++);
- current_instructions += Constant(expression.value());
+ const TokenPosition pos = ReadPosition(); // read jth position.
+ current_instructions += Constant(
+ Instance::ZoneHandle(Z, constant_reader_.ReadConstantExpression()));
current_instructions += LoadLocal(scopes()->switch_variable);
- current_instructions += InstanceCall(
- expression.position(), Symbols::EqualOperator(), Token::kEQ,
- /*argument_count=*/2,
- /*checked_argument_count=*/2);
+ current_instructions +=
+ InstanceCall(pos, Symbols::EqualOperator(), Token::kEQ,
+ /*argument_count=*/2,
+ /*checked_argument_count=*/2);
current_instructions += BranchIfTrue(&then, &otherwise, false);
Fragment then_fragment(then);
@@ -4831,23 +4794,23 @@
// join instruction).
then_fragment += Goto(body_join);
} else {
- // There is only a single branch to the body, so we will just append
+ // There is only a signle branch to the body, so we will just append
// the body fragment.
- then_fragment += case_bodies[i];
+ then_fragment += body_fragments[i];
}
- current_instructions = Fragment(current_instructions.entry, otherwise);
+ current_instructions = Fragment(otherwise);
}
}
}
- if (case_count > 0 && !helper->has_default()) {
+ if (case_count > 0 && default_case < 0) {
// There is no default, which means we have an open [current_instructions]
// (which is a [TargetEntryInstruction] for the last "otherwise" branch).
//
// Furthermore the last [SwitchCase] can be open as well. If so, we need
// to join these two.
- Fragment& last_body = case_bodies[case_count - 1];
+ Fragment& last_body = body_fragments[case_count - 1];
if (last_body.is_open()) {
ASSERT(current_instructions.is_open());
ASSERT(current_instructions.current->IsTargetEntry());
@@ -4857,7 +4820,7 @@
current_instructions += Goto(join);
last_body += Goto(join);
- current_instructions = Fragment(current_instructions.entry, join);
+ current_instructions = Fragment(join);
}
} else {
// All non-default cases will be closed (i.e. break/continue/throw/return)
@@ -4865,354 +4828,8 @@
// default case.
}
- return current_instructions;
-}
-
-Fragment StreamingFlowGraphBuilder::BuildOptimizedSwitchPrelude(
- SwitchHelper* helper,
- JoinEntryInstr* join) {
- const TokenPosition pos = helper->position();
-
- // We need to check that the switch variable is of the correct type.
- // If it is not, we go to [join] which is either the default case or
- // the exit of the switch statement.
-
- TargetEntryInstr* then_entry;
- TargetEntryInstr* otherwise_entry;
-
- const AbstractType& expression_type =
- AbstractType::ZoneHandle(Z, helper->expression_class().RareType());
- ASSERT(dart::SimpleInstanceOfType(expression_type));
-
- Fragment instructions;
- instructions += LoadLocal(scopes()->switch_variable);
- instructions += Constant(expression_type);
- instructions += InstanceCall(
- pos, Library::PrivateCoreLibName(Symbols::_simpleInstanceOf()),
- Token::kIS, /*argument_count=*/2,
- /*checked_argument_count=*/2);
- instructions += BranchIfTrue(&then_entry, &otherwise_entry, /*negate=*/false);
-
- Fragment otherwise_instructions(otherwise_entry);
- otherwise_instructions += Goto(join);
-
- instructions = Fragment(instructions.entry, then_entry);
-
- if (helper->is_enum_switch()) {
- // For an enum switch, we need to load the enum index from the switch
- // variable.
-
- instructions += LoadLocal(scopes()->switch_variable);
- const Field& enum_index_field =
- Field::ZoneHandle(Z, IG->object_store()->enum_index_field());
- instructions += B->LoadField(enum_index_field, /*calls_initializer=*/false);
- instructions += StoreLocal(pos, scopes()->switch_variable);
- instructions += Drop();
- }
-
- return instructions;
-}
-
-Fragment StreamingFlowGraphBuilder::BuildBinarySearchSwitch(
- SwitchHelper* helper) {
- // * We build a binary tree of conditional branches where each branch bisects
- // the remaining cases.
- // * At holes in the switch expression range we need to add additional
- // bound checks.
- // * At each leaf we add the body of the case or a goto, if the case has
- // jumpers.
- // * Leafs at the bounds of the switch expression range might need to
- // do a bound check.
-
- SwitchBlock* block = helper->switch_block();
- const intptr_t case_count = helper->case_count();
- const intptr_t default_case = helper->default_case();
- const GrowableArray<Fragment>& case_bodies = helper->case_bodies();
- const intptr_t expression_count = helper->expressions().length();
- const GrowableArray<SwitchExpression*>& sorted_expressions =
- helper->sorted_expressions();
- TargetEntryInstr* then_entry;
- TargetEntryInstr* otherwise_entry;
-
- // Entry to the default case or the exit of the switch, if there is no
- // default case.
- JoinEntryInstr* join;
- if (helper->has_default()) {
- join = block->DestinationDirect(default_case);
- } else {
- join = BuildJoinEntry();
- }
-
- Fragment join_instructions(join);
- if (helper->has_default()) {
- join_instructions += case_bodies.At(default_case);
- }
-
- Fragment current_instructions = BuildOptimizedSwitchPrelude(helper, join);
-
- GrowableArray<SwitchRange> stack;
- stack.Add(SwitchRange::Branch(0, expression_count - 1, current_instructions));
-
- while (!stack.is_empty()) {
- const SwitchRange range = stack.RemoveLast();
- Fragment branch_instructions = range.branch_instructions();
-
- if (range.is_leaf()) {
- const intptr_t expression_index = range.min();
- const SwitchExpression& expression =
- *sorted_expressions.At(expression_index);
-
- if (!range.is_bounds_checked() &&
- ((helper->RequiresLowerBoundCheck() && expression_index == 0) ||
- (helper->RequiresUpperBoundCheck() &&
- expression_index == expression_count - 1))) {
- // This leaf needs a bound check.
-
- branch_instructions += LoadLocal(scopes()->switch_variable);
- branch_instructions += Constant(expression.integer());
- branch_instructions +=
- StrictCompare(expression.position(), Token::kEQ_STRICT,
- /*number_check=*/true);
- branch_instructions +=
- BranchIfTrue(&then_entry, &otherwise_entry, /*negate=*/false);
-
- Fragment otherwise_instructions(otherwise_entry);
- otherwise_instructions += Goto(join);
-
- stack.Add(SwitchRange::Leaf(expression_index, Fragment(then_entry),
- /*is_bounds_checked=*/true));
- } else {
- // We are at a leaf where we can add the body of the case or a goto to
- // [join].
-
- const intptr_t case_index = expression.case_index();
-
- if (case_index == default_case) {
- branch_instructions += Goto(join);
- } else {
- if (block->HadJumper(case_index)) {
- JoinEntryInstr* join = block->DestinationDirect(case_index);
- branch_instructions += Goto(join);
-
- if (join->next() == nullptr) {
- // The first time we reach an expression that jumps to a case
- // body we emit the body.
- branch_instructions = Fragment(join);
- branch_instructions += case_bodies.At(case_index);
- }
- } else {
- branch_instructions += case_bodies.At(case_index);
- }
-
- if (!helper->has_default() && case_index == case_count - 1) {
- if (branch_instructions.is_open()) {
- branch_instructions += Goto(join);
- }
- }
- }
-
- ASSERT(branch_instructions.is_closed());
- }
- } else {
- // Add a conditional to bisect the range.
-
- const intptr_t middle = range.min() + (range.max() - range.min()) / 2;
- const intptr_t next = middle + 1;
- const SwitchExpression& middle_expression =
- *sorted_expressions.At(middle);
- const SwitchExpression& next_expression = *sorted_expressions.At(next);
-
- branch_instructions += LoadLocal(scopes()->switch_variable);
- branch_instructions += Constant(middle_expression.integer());
- branch_instructions +=
- InstanceCall(middle_expression.position(),
- Symbols::LessEqualOperator(), Token::kLTE,
- /*argument_count=*/2,
- /*checked_argument_count=*/2);
- branch_instructions +=
- BranchIfTrue(&then_entry, &otherwise_entry, /*negate=*/false);
-
- Fragment lower_branch_instructions(then_entry);
- Fragment upper_branch_instructions(otherwise_entry);
-
- if (next_expression.integer().AsInt64Value() >
- middle_expression.integer().AsInt64Value() + 1) {
- // The upper branch is not contiguous with the lower branch.
- // Before continuing in the upper branch we add a bound check.
-
- upper_branch_instructions += LoadLocal(scopes()->switch_variable);
- upper_branch_instructions += Constant(next_expression.integer());
- upper_branch_instructions +=
- InstanceCall(next_expression.position(),
- Symbols::GreaterEqualOperator(), Token::kGTE,
- /*argument_count=*/2,
- /*checked_argument_count=*/2);
- upper_branch_instructions +=
- BranchIfTrue(&then_entry, &otherwise_entry, /*negate=*/false);
-
- Fragment otherwise_instructions(otherwise_entry);
- otherwise_instructions += Goto(join);
-
- upper_branch_instructions = Fragment(then_entry);
- }
-
- stack.Add(
- SwitchRange::Branch(next, range.max(), upper_branch_instructions));
- stack.Add(
- SwitchRange::Branch(range.min(), middle, lower_branch_instructions));
- }
- }
-
- return Fragment(current_instructions.entry, join_instructions.current);
-}
-
-Fragment StreamingFlowGraphBuilder::BuildJumpTableSwitch(SwitchHelper* helper) {
- // * If input value is not integer or enum value, goto default case or
- // switch exit.
- // * If value is enum value, load its index.
- // * If input integer is outside of jump table range, goto default case
- // or switch exit.
- // * Jump to case with jump table.
- // * For each expression, add entry to jump to case.
- // * For each hole in the integer range, add entry to jump to default
- // cause or switch exit.
-
- SwitchBlock* block = helper->switch_block();
- const TokenPosition pos = helper->position();
- const intptr_t case_count = helper->case_count();
- const intptr_t default_case = helper->default_case();
- const GrowableArray<Fragment>& case_bodies = helper->case_bodies();
- const Integer& expression_min = helper->expression_min();
- const Integer& expression_max = helper->expression_max();
- TargetEntryInstr* then_entry;
- TargetEntryInstr* otherwise_entry;
-
- // Entry to the default case or the exit of the switch, if there is no
- // default case.
- JoinEntryInstr* join;
- if (helper->has_default()) {
- join = block->DestinationDirect(default_case);
- } else {
- join = BuildJoinEntry();
- }
-
- Fragment join_instructions(join);
-
- Fragment current_instructions = BuildOptimizedSwitchPrelude(helper, join);
-
- if (helper->RequiresLowerBoundCheck()) {
- current_instructions += LoadLocal(scopes()->switch_variable);
- current_instructions += Constant(expression_min);
- current_instructions += InstanceCall(pos, Symbols::GreaterEqualOperator(),
- Token::kGTE, /*argument_count=*/2,
- /*checked_argument_count=*/2);
- current_instructions += BranchIfTrue(&then_entry, &otherwise_entry,
- /*negate=*/false);
- Fragment otherwise_instructions(otherwise_entry);
- otherwise_instructions += Goto(join);
-
- current_instructions = Fragment(current_instructions.entry, then_entry);
- }
-
- if (helper->RequiresUpperBoundCheck()) {
- current_instructions += LoadLocal(scopes()->switch_variable);
- current_instructions += Constant(expression_max);
- current_instructions += InstanceCall(pos, Symbols::LessEqualOperator(),
- Token::kLTE, /*argument_count=*/2,
- /*checked_argument_count=*/2);
- current_instructions += BranchIfTrue(&then_entry, &otherwise_entry,
- /*negate=*/false);
- Fragment otherwise_instructions(otherwise_entry);
- otherwise_instructions += Goto(join);
-
- current_instructions = Fragment(current_instructions.entry, then_entry);
- }
-
- current_instructions += LoadLocal(scopes()->switch_variable);
-
- if (!expression_min.IsZero()) {
- // Adjust for the range of the jump table, which starts at 0.
- current_instructions += Constant(expression_min);
- current_instructions +=
- InstanceCall(pos, Symbols::Minus(), Token::kSUB, /*argument_count=*/2,
- /*checked_argument_count=*/2);
- }
-
- const intptr_t table_size = helper->ExpressionRange();
- IndirectGotoInstr* indirect_goto = IndirectGoto(table_size);
- current_instructions <<= indirect_goto;
- current_instructions = current_instructions.closed();
-
- GrowableArray<TargetEntryInstr*> table_entries(table_size);
- table_entries.FillWith(nullptr, 0, table_size);
-
- // Generate the jump table entries for the switch cases.
- intptr_t expression_index = 0;
- for (intptr_t i = 0; i < case_count; ++i) {
- const int expression_count = helper->case_expression_counts().At(i);
-
- // Generate jump table entries for each case expression.
- if (i != default_case) {
- for (intptr_t j = 0; j < expression_count; ++j) {
- const SwitchExpression& expression =
- helper->expressions().At(expression_index++);
- const intptr_t table_offset =
- expression.integer().AsInt64Value() - expression_min.AsInt64Value();
-
- IndirectEntryInstr* indirect_entry =
- B->BuildIndirectEntry(table_offset, CurrentTryIndex());
- Fragment indirect_entry_instructions(indirect_entry);
- indirect_entry_instructions += Goto(block->DestinationDirect(i));
-
- TargetEntryInstr* entry = B->BuildTargetEntry();
- Fragment entry_instructions(entry);
- entry_instructions += Goto(indirect_entry);
-
- table_entries[table_offset] = entry;
- }
- }
-
- // Connect the case body to its join entry.
- if (i == default_case) {
- join_instructions += case_bodies.At(i);
- } else {
- Fragment case_instructions(block->DestinationDirect(i));
- case_instructions += case_bodies.At(i);
-
- if (i == case_count - 1) {
- // If the last case is not the default case and it is still open
- // close it by going to the exit of the switch.
- if (case_instructions.is_open()) {
- case_instructions += Goto(join);
- }
- }
-
- ASSERT(case_instructions.is_closed());
- }
- }
-
- // Generate the jump table entries for holes in the integer range.
- for (intptr_t i = 0; i < table_size; i++) {
- if (table_entries.At(i) == nullptr) {
- IndirectEntryInstr* indirect_entry =
- B->BuildIndirectEntry(i, CurrentTryIndex());
- Fragment indirect_entry_instructions(indirect_entry);
- indirect_entry_instructions += Goto(join);
-
- TargetEntryInstr* entry = flow_graph_builder_->BuildTargetEntry();
- Fragment entry_instructions(entry);
- entry_instructions += Goto(indirect_entry);
-
- table_entries[i] = entry;
- }
- }
-
- // Add the jump table entries to the jump table.
- for (intptr_t i = 0; i < table_size; i++) {
- indirect_goto->AddSuccessor(table_entries.At(i));
- }
-
- return Fragment(current_instructions.entry, join_instructions.current);
+ SetOffset(end_offset);
+ return Fragment(head_instructions.entry, current_instructions.current);
}
Fragment StreamingFlowGraphBuilder::BuildContinueSwitchStatement(
@@ -5220,7 +4837,7 @@
const TokenPosition pos = ReadPosition(); // read position.
if (position != nullptr) *position = pos;
- intptr_t target_index = ReadUInt(); // read target index.
+ intptr_t target_index = ReadUInt(); // read target index.
TryFinallyBlock* outer_finally = nullptr;
intptr_t target_context_depth = -1;
diff --git a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.h b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.h
index 22da387..6daf672 100644
--- a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.h
+++ b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.h
@@ -152,7 +152,7 @@
void InlineBailout(const char* reason);
Fragment DebugStepCheck(TokenPosition position);
Fragment LoadLocal(LocalVariable* variable);
- IndirectGotoInstr* IndirectGoto(intptr_t target_count);
+ Fragment IndirectGoto(intptr_t target_count);
Fragment Return(
TokenPosition position,
intptr_t yield_index = UntaggedPcDescriptors::kInvalidYieldIndex);
@@ -349,12 +349,6 @@
Fragment BuildForStatement(TokenPosition* position);
Fragment BuildForInStatement(bool async, TokenPosition* position);
Fragment BuildSwitchStatement(TokenPosition* position);
- Fragment BuildSwitchCase(SwitchHelper* helper, intptr_t case_index);
- Fragment BuildLinearScanSwitch(SwitchHelper* helper);
- Fragment BuildOptimizedSwitchPrelude(SwitchHelper* helper,
- JoinEntryInstr* join);
- Fragment BuildBinarySearchSwitch(SwitchHelper* helper);
- Fragment BuildJumpTableSwitch(SwitchHelper* helper);
Fragment BuildContinueSwitchStatement(TokenPosition* position);
Fragment BuildIfStatement(TokenPosition* position);
Fragment BuildReturnStatement(TokenPosition* position);
diff --git a/runtime/vm/compiler/frontend/kernel_to_il.cc b/runtime/vm/compiler/frontend/kernel_to_il.cc
index 2a553e6..eba8dba 100644
--- a/runtime/vm/compiler/frontend/kernel_to_il.cc
+++ b/runtime/vm/compiler/frontend/kernel_to_il.cc
@@ -45,12 +45,6 @@
false,
"Print huge methods (less optimized)");
-DEFINE_FLAG(int,
- force_switch_dispatch_type,
- -1,
- "Force switch statements to use a particular dispatch type: "
- "-1=auto, 0=linear scan, 1=binary search, 2=jump table");
-
namespace kernel {
#define Z (zone_)
@@ -482,9 +476,9 @@
}
}
-IndirectGotoInstr* FlowGraphBuilder::IndirectGoto(intptr_t target_count) {
+Fragment FlowGraphBuilder::IndirectGoto(intptr_t target_count) {
Value* index = Pop();
- return new (Z) IndirectGotoInstr(target_count, index);
+ return Fragment(new (Z) IndirectGotoInstr(target_count, index));
}
Fragment FlowGraphBuilder::ThrowLateInitializationError(
@@ -4960,206 +4954,6 @@
return prepend_type_arguments_;
}
-int64_t SwitchHelper::ExpressionRange() const {
- const int64_t min = expression_min().AsInt64Value();
- const int64_t max = expression_max().AsInt64Value();
- ASSERT(min <= max);
- const uint64_t diff = static_cast<uint64_t>(max) - static_cast<uint64_t>(min);
- // Saturate to avoid overflow.
- if (diff > static_cast<uint64_t>(kMaxInt64 - 1)) {
- return kMaxInt64;
- }
- return static_cast<int64_t>(diff + 1);
-}
-
-bool SwitchHelper::RequiresLowerBoundCheck() const {
- if (is_enum_switch()) {
- if (expression_min().IsZero()) {
- // Enum indexes are always positive.
- return false;
- }
- }
- return true;
-}
-
-bool SwitchHelper::RequiresUpperBoundCheck() const {
- if (is_enum_switch()) {
- return has_default() || !is_exhaustive();
- }
- return true;
-}
-
-SwitchDispatch SwitchHelper::SelectDispatchStrategy() {
- // For small to medium-sized switches, binary search is faster than a
- // jump table.
- // Please update runtime/tests/vm/dart/optimized_switch_test.dart
- // when changing this constant.
- const intptr_t kJumpTableMinExpressions = 16;
- // This limit comes from IndirectGotoInstr.
- // Realistically, the current limit should never be hit by any code.
- const intptr_t kJumpTableMaxSize = kMaxInt32;
- // Sometimes the switch expressions don't cover a contiguous range.
- // If the ratio of holes to expressions is too great we fall back to a
- // binary search to avoid code size explosion.
- const double kJumpTableMaxHolesRatio = 1.0;
-
- if (!is_optimizable()) {
- // The switch is not optimizable, so we can only use linear scan.
- return kSwitchDispatchLinearScan;
- }
-
- if (FLAG_force_switch_dispatch_type == kSwitchDispatchLinearScan) {
- return kSwitchDispatchLinearScan;
- }
-
- PrepareForOptimizedSwitch();
-
- if (!is_optimizable()) {
- // While preparing for an optimized switch we might have discovered that
- // the switch is not optimizable after all.
- return kSwitchDispatchLinearScan;
- }
-
- if (FLAG_force_switch_dispatch_type == kSwitchDispatchBinarySearch) {
- return kSwitchDispatchBinarySearch;
- }
-
- const int64_t range = ExpressionRange();
- if (range > kJumpTableMaxSize) {
- return kSwitchDispatchBinarySearch;
- }
-
- const intptr_t num_expressions = expressions().length();
- ASSERT(num_expressions <= range);
-
- const intptr_t max_holes = num_expressions * kJumpTableMaxHolesRatio;
- const int64_t holes = range - num_expressions;
-
- if (FLAG_force_switch_dispatch_type != kSwitchDispatchJumpTable) {
- if (num_expressions < kJumpTableMinExpressions) {
- return kSwitchDispatchBinarySearch;
- }
-
- if (holes > max_holes) {
- return kSwitchDispatchBinarySearch;
- }
- }
-
- // After this point we will use a jump table.
-
- // In the general case, bounds checks are required before a jump table
- // to handle all possible integer values.
- // For enums, the set of possible index values is known and much smaller
- // than the set of all possible integer values. A jump table that covers
- // either or both bounds of the range of index values requires only one or
- // no bounds checks.
- // If the expressions of an enum switch don't cover the full range of
- // values we can try to extend the jump table to cover the full range, but
- // not beyond kJumpTableMaxHolesRatio.
- // The count of enum values is not available when the flow graph is
- // constructed. The lower bound is always 0 so eliminating the lower
- // bound check is still possible by extending expression_min to 0.
- //
- // In the case of an integer switch we try to extend expression_min to 0
- // for a different reason.
- // If the range starts at zero it directly maps to the jump table
- // and we don't need to adjust the switch variable before the
- // jump table.
- if (expression_min().AsInt64Value() > 0) {
- const intptr_t holes_budget = Utils::Minimum(
- // Holes still available.
- max_holes - holes,
- // Entries left in the jump table.
- kJumpTableMaxSize - range);
-
- const int64_t required_holes = expression_min().AsInt64Value();
- if (required_holes <= holes_budget) {
- expression_min_ = &Object::smi_zero();
- }
- }
-
- return kSwitchDispatchJumpTable;
-}
-
-void SwitchHelper::PrepareForOptimizedSwitch() {
- // Find the min and max of integer representations of expressions.
- // We also populate SwitchExpressions.integer for later use.
- const Field* enum_index_field = nullptr;
- for (intptr_t i = 0; i < expressions_.length(); ++i) {
- SwitchExpression& expression = expressions_[i];
- sorted_expressions_.Add(&expression);
-
- const Instance& value = expression.value();
- const Integer* integer = nullptr;
- if (is_enum_switch()) {
- if (enum_index_field == nullptr) {
- enum_index_field = &Field::Handle(
- zone_, IsolateGroup::Current()->object_store()->enum_index_field());
- }
- integer = &Integer::ZoneHandle(
- zone_, Integer::RawCast(value.GetField(*enum_index_field)));
- } else {
- integer = &Integer::Cast(value);
- }
- expression.set_integer(*integer);
- if (i == 0) {
- expression_min_ = integer;
- expression_max_ = integer;
- } else {
- if (expression_min_->CompareWith(*integer) > 0) {
- expression_min_ = integer;
- }
- if (expression_max_->CompareWith(*integer) < 0) {
- expression_max_ = integer;
- }
- }
- }
-
- // Sort expressions by their integer value.
- sorted_expressions_.Sort(
- [](SwitchExpression* const* a, SwitchExpression* const* b) {
- return (*a)->integer().CompareWith((*b)->integer());
- });
-
- // Check that there are no duplicate case expressions.
- // Duplicate expressions are allowed in switch statements, but
- // optimized switches don't implemented them.
- for (intptr_t i = 0; i < sorted_expressions_.length() - 1; ++i) {
- const SwitchExpression& a = *sorted_expressions_.At(i);
- const SwitchExpression& b = *sorted_expressions_.At(i + 1);
- if (a.integer().Equals(b.integer())) {
- is_optimizable_ = false;
- break;
- }
- }
-}
-
-void SwitchHelper::AddExpression(intptr_t case_index,
- TokenPosition position,
- const Instance& value) {
- case_expression_counts_[case_index]++;
-
- expressions_.Add(SwitchExpression(case_index, position, value));
-
- if (is_optimizable_ || expression_class_ == nullptr) {
- // Check the type of the expression for use in an optimized switch.
- const Class& value_class = Class::ZoneHandle(zone_, value.clazz());
- if (expression_class_ == nullptr) {
- if (value.IsInteger() || value_class.is_enum_class()) {
- expression_class_ = &value_class;
- is_optimizable_ = true;
- } else {
- // At least one expression (the first) is not suitable for an
- // optimized switch.
- is_optimizable_ = false;
- }
- } else if (value_class.ptr() != expression_class_->ptr()) {
- // At least one expression has a different type than the others.
- is_optimizable_ = false;
- }
- }
-}
-
} // namespace kernel
} // namespace dart
diff --git a/runtime/vm/compiler/frontend/kernel_to_il.h b/runtime/vm/compiler/frontend/kernel_to_il.h
index 69471ce..045750e 100644
--- a/runtime/vm/compiler/frontend/kernel_to_il.h
+++ b/runtime/vm/compiler/frontend/kernel_to_il.h
@@ -19,7 +19,6 @@
#include "vm/compiler/frontend/base_flow_graph_builder.h"
#include "vm/compiler/frontend/kernel_translation_helper.h"
#include "vm/compiler/frontend/scope_builder.h"
-#include "vm/object_store.h"
namespace dart {
@@ -194,7 +193,7 @@
Fragment RethrowException(TokenPosition position, int catch_try_index);
Fragment LoadLocal(LocalVariable* variable);
- IndirectGotoInstr* IndirectGoto(intptr_t target_count);
+ Fragment IndirectGoto(intptr_t target_count);
Fragment StoreLateField(const Field& field,
LocalVariable* instance,
LocalVariable* setter_value);
@@ -936,190 +935,6 @@
DISALLOW_COPY_AND_ASSIGN(CatchBlock);
};
-enum SwitchDispatch {
- kSwitchDispatchAuto = -1,
- kSwitchDispatchLinearScan,
- kSwitchDispatchBinarySearch,
- kSwitchDispatchJumpTable,
-};
-
-// Collected information for a switch expression.
-class SwitchExpression {
- public:
- SwitchExpression(intptr_t case_index,
- TokenPosition position,
- const Instance& value)
- : case_index_(case_index), position_(position), value_(&value) {}
-
- intptr_t case_index() const { return case_index_; }
- const TokenPosition& position() const { return position_; }
- // Constant value of the expression.
- const Instance& value() const { return *value_; }
-
- // Integer representation of the expression.
- // For Integers it is the value itself and for Enums it is the index.
- const Integer& integer() const {
- ASSERT(integer_ != nullptr);
- return *integer_;
- }
-
- void set_integer(const Integer& integer) {
- ASSERT(integer_ == nullptr);
- integer_ = &integer;
- }
-
- private:
- intptr_t case_index_;
- TokenPosition position_;
- const Instance* value_;
- const Integer* integer_ = nullptr;
-};
-
-// A range that is covered by a branch in a binary search switch.
-// Leafs are represented by a range where min == max.
-class SwitchRange {
- public:
- static SwitchRange Leaf(intptr_t index,
- Fragment branch_instructions,
- bool is_bounds_checked = false) {
- return SwitchRange(index, index, branch_instructions, is_bounds_checked);
- }
-
- static SwitchRange Branch(intptr_t min,
- intptr_t max,
- Fragment branch_instructions) {
- return SwitchRange(min, max, branch_instructions,
- /*is_bounds_checked=*/false);
- }
-
- // min and max are indexes into a sorted array of case expressions.
- intptr_t min() const { return min_; }
- intptr_t max() const { return max_; }
- // The fragment to continue building code for the branch.
- Fragment branch_instructions() const { return branch_instructions_; }
- // For leafs, whether the branch is known to be in the bounds of the
- // overall switch.
- bool is_bounds_checked() const { return is_bounds_checked_; }
- bool is_leaf() const { return min_ == max_; }
-
- private:
- SwitchRange(intptr_t min,
- intptr_t max,
- Fragment branch_instructions,
- bool is_bounds_checked)
- : min_(min),
- max_(max),
- branch_instructions_(branch_instructions),
- is_bounds_checked_(is_bounds_checked) {}
-
- intptr_t min_;
- intptr_t max_;
- Fragment branch_instructions_;
- bool is_bounds_checked_;
-};
-
-// Helper for building flow graph for a switch statement.
-class SwitchHelper {
- public:
- SwitchHelper(Zone* zone,
- TokenPosition position,
- bool is_exhaustive,
- SwitchBlock* switch_block,
- intptr_t case_count)
- : zone_(zone),
- position_(position),
- is_exhaustive_(is_exhaustive),
- switch_block_(switch_block),
- case_count_(case_count),
- case_bodies_(case_count),
- case_expression_counts_(case_count),
- expressions_(case_count),
- sorted_expressions_(case_count) {
- case_expression_counts_.FillWith(0, 0, case_count);
- }
-
- // A switch statement is optimizable if all expression are of the same type
- // and have integer representations.
- bool is_optimizable() const { return is_optimizable_; }
- const TokenPosition& position() const { return position_; }
- bool is_exhaustive() const { return is_exhaustive_; }
- SwitchBlock* switch_block() { return switch_block_; }
- intptr_t case_count() const { return case_count_; }
-
- // Index of default case.
- intptr_t default_case() const { return default_case_; }
- void set_default_case(intptr_t index) {
- ASSERT(default_case_ == -1);
- default_case_ = index;
- }
-
- const GrowableArray<Fragment>& case_bodies() const { return case_bodies_; }
-
- // Array of the expression counts for all cases.
- const GrowableArray<intptr_t>& case_expression_counts() const {
- return case_expression_counts_;
- }
-
- const GrowableArray<SwitchExpression>& expressions() const {
- return expressions_;
- }
-
- const GrowableArray<SwitchExpression*>& sorted_expressions() const {
- return sorted_expressions_;
- }
-
- // The common class of all expressions. The statement must be optimizable.
- const Class& expression_class() const {
- ASSERT(expression_class_ != nullptr);
- return *expression_class_;
- }
-
- const Integer& expression_min() const {
- ASSERT(expression_min_ != nullptr);
- return *expression_min_;
- }
- const Integer& expression_max() const {
- ASSERT(expression_max_ != nullptr);
- return *expression_max_;
- }
-
- bool has_default() const { return default_case_ >= 0; }
-
- bool is_enum_switch() const { return expression_class().is_enum_class(); }
-
- // Returns size of [min..max] range, or kMaxInt64 on overflow.
- int64_t ExpressionRange() const;
-
- bool RequiresLowerBoundCheck() const;
- bool RequiresUpperBoundCheck() const;
-
- SwitchDispatch SelectDispatchStrategy();
-
- void AddCaseBody(Fragment body) { case_bodies_.Add(body); }
-
- void AddExpression(intptr_t case_index,
- TokenPosition position,
- const Instance& value);
-
- private:
- void PrepareForOptimizedSwitch();
-
- Zone* zone_;
- bool is_optimizable_ = false;
- const TokenPosition position_;
- const bool is_exhaustive_;
- SwitchBlock* const switch_block_;
- const intptr_t case_count_;
- intptr_t default_case_ = -1;
- GrowableArray<Fragment> case_bodies_;
- GrowableArray<intptr_t> case_expression_counts_;
- GrowableArray<SwitchExpression> expressions_;
- GrowableArray<SwitchExpression*> sorted_expressions_;
- const Class* expression_class_ = nullptr;
- const Integer* expression_min_ = nullptr;
- const Integer* expression_max_ = nullptr;
-};
-
} // namespace kernel
} // namespace dart
diff --git a/tests/language/switch/backward_jump_test.dart b/tests/language/switch/backward_jump_test.dart
index dbcde00..d4bc491 100644
--- a/tests/language/switch/backward_jump_test.dart
+++ b/tests/language/switch/backward_jump_test.dart
@@ -2,11 +2,6 @@
// 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.
-// VMOptions=
-// VMOptions=--force-switch-dispatch-type=0
-// VMOptions=--force-switch-dispatch-type=1
-// VMOptions=--force-switch-dispatch-type=2
-
import 'package:expect/expect.dart';
main() {
diff --git a/tests/language/switch/empty_block_case_test.dart b/tests/language/switch/empty_block_case_test.dart
index 4fbc593..6edaff4 100644
--- a/tests/language/switch/empty_block_case_test.dart
+++ b/tests/language/switch/empty_block_case_test.dart
@@ -6,11 +6,6 @@
// Test that a case with an empty block does not fall through.
-// VMOptions=
-// VMOptions=--force-switch-dispatch-type=0
-// VMOptions=--force-switch-dispatch-type=1
-// VMOptions=--force-switch-dispatch-type=2
-
class EmptyBlockCaseTest {
static testMain() {
var exception = null;
diff --git a/tests/language/switch/fallthru_runtime_test.dart b/tests/language/switch/fallthru_runtime_test.dart
index 373c93f..b1dd57d 100644
--- a/tests/language/switch/fallthru_runtime_test.dart
+++ b/tests/language/switch/fallthru_runtime_test.dart
@@ -4,14 +4,8 @@
// Copyright (c) 2011, 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.
-
// Check that FallThroughError is thrown if switch clause does not terminate.
-// VMOptions=
-// VMOptions=--force-switch-dispatch-type=0
-// VMOptions=--force-switch-dispatch-type=1
-// VMOptions=--force-switch-dispatch-type=2
-
import "package:expect/expect.dart";
String test(int n) {
diff --git a/tests/language/switch/infinite_switch_label_test.dart b/tests/language/switch/infinite_switch_label_test.dart
index 816a52c..7f484cc 100644
--- a/tests/language/switch/infinite_switch_label_test.dart
+++ b/tests/language/switch/infinite_switch_label_test.dart
@@ -4,11 +4,6 @@
// Test nested switch statement using labels.
-// VMOptions=
-// VMOptions=--force-switch-dispatch-type=0
-// VMOptions=--force-switch-dispatch-type=1
-// VMOptions=--force-switch-dispatch-type=2
-
library nested_switch_label;
import "package:expect/expect.dart";
diff --git a/tests/language/switch/label2_test.dart b/tests/language/switch/label2_test.dart
index e03084d..612e6a9 100644
--- a/tests/language/switch/label2_test.dart
+++ b/tests/language/switch/label2_test.dart
@@ -4,11 +4,6 @@
// Test switch statement using labels.
-// VMOptions=
-// VMOptions=--force-switch-dispatch-type=0
-// VMOptions=--force-switch-dispatch-type=1
-// VMOptions=--force-switch-dispatch-type=2
-
import 'package:expect/expect.dart';
void main() {
diff --git a/tests/language/switch/label_test.dart b/tests/language/switch/label_test.dart
index 3a36837..290c84f 100644
--- a/tests/language/switch/label_test.dart
+++ b/tests/language/switch/label_test.dart
@@ -1,14 +1,8 @@
// Copyright (c) 2011, 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.
-
// Test switch statement using labels.
-// VMOptions=
-// VMOptions=--force-switch-dispatch-type=0
-// VMOptions=--force-switch-dispatch-type=1
-// VMOptions=--force-switch-dispatch-type=2
-
import "package:expect/expect.dart";
class Switcher {
diff --git a/tests/language/switch/nested_switch_label_test.dart b/tests/language/switch/nested_switch_label_test.dart
index efc27a2..49c2754 100644
--- a/tests/language/switch/nested_switch_label_test.dart
+++ b/tests/language/switch/nested_switch_label_test.dart
@@ -4,11 +4,6 @@
// Test nested switch statement using labels.
-// VMOptions=
-// VMOptions=--force-switch-dispatch-type=0
-// VMOptions=--force-switch-dispatch-type=1
-// VMOptions=--force-switch-dispatch-type=2
-
library nested_switch_label;
import "package:expect/expect.dart";
diff --git a/tests/language/switch/scope_test.dart b/tests/language/switch/scope_test.dart
index 4ca4c3b..938a393 100644
--- a/tests/language/switch/scope_test.dart
+++ b/tests/language/switch/scope_test.dart
@@ -1,14 +1,8 @@
// Copyright (c) 2011, 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.
-
// Test that a new scope is introduced for each switch case.
-// VMOptions=
-// VMOptions=--force-switch-dispatch-type=0
-// VMOptions=--force-switch-dispatch-type=1
-// VMOptions=--force-switch-dispatch-type=2
-
import "package:expect/expect.dart";
class SwitchScopeTest {
diff --git a/tests/language/switch/switch6_test.dart b/tests/language/switch/switch6_test.dart
index 32edea5..43e09dc 100644
--- a/tests/language/switch/switch6_test.dart
+++ b/tests/language/switch/switch6_test.dart
@@ -1,14 +1,8 @@
// Copyright (c) 2011, 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.
-
// The break is in the right scope, http://b/3428700 was agreed upon.
-// VMOptions=
-// VMOptions=--force-switch-dispatch-type=0
-// VMOptions=--force-switch-dispatch-type=1
-// VMOptions=--force-switch-dispatch-type=2
-
import "package:expect/expect.dart";
class Switch6Test {
diff --git a/tests/language/switch/switch_test.dart b/tests/language/switch/switch_test.dart
index 3198957..032c2b7 100644
--- a/tests/language/switch/switch_test.dart
+++ b/tests/language/switch/switch_test.dart
@@ -1,14 +1,8 @@
// Copyright (c) 2012, 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.
-
// Test switch statement.
-// VMOptions=
-// VMOptions=--force-switch-dispatch-type=0
-// VMOptions=--force-switch-dispatch-type=1
-// VMOptions=--force-switch-dispatch-type=2
-
import "package:expect/expect.dart";
class Switcher {
diff --git a/tests/language/switch/try_catch_test.dart b/tests/language/switch/try_catch_test.dart
index 56632e7..beee717 100644
--- a/tests/language/switch/try_catch_test.dart
+++ b/tests/language/switch/try_catch_test.dart
@@ -5,11 +5,6 @@
// Regression test for issue 18869: Check that try-catch is working correctly
// inside switch-case clauses.
-// VMOptions=
-// VMOptions=--force-switch-dispatch-type=0
-// VMOptions=--force-switch-dispatch-type=1
-// VMOptions=--force-switch-dispatch-type=2
-
import "package:expect/expect.dart";
test_switch() {
diff --git a/tests/language_2/switch/backward_jump_test.dart b/tests/language_2/switch/backward_jump_test.dart
index dbcde00..d4bc491 100644
--- a/tests/language_2/switch/backward_jump_test.dart
+++ b/tests/language_2/switch/backward_jump_test.dart
@@ -2,11 +2,6 @@
// 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.
-// VMOptions=
-// VMOptions=--force-switch-dispatch-type=0
-// VMOptions=--force-switch-dispatch-type=1
-// VMOptions=--force-switch-dispatch-type=2
-
import 'package:expect/expect.dart';
main() {
diff --git a/tests/language_2/switch/empty_block_case_test.dart b/tests/language_2/switch/empty_block_case_test.dart
index 9b28adc..5e9cfed 100644
--- a/tests/language_2/switch/empty_block_case_test.dart
+++ b/tests/language_2/switch/empty_block_case_test.dart
@@ -8,11 +8,6 @@
// Test that a case with an empty block does not fall through.
-// VMOptions=
-// VMOptions=--force-switch-dispatch-type=0
-// VMOptions=--force-switch-dispatch-type=1
-// VMOptions=--force-switch-dispatch-type=2
-
class EmptyBlockCaseTest {
static testMain() {
var exception = null;
diff --git a/tests/language_2/switch/fallthru_runtime_test.dart b/tests/language_2/switch/fallthru_runtime_test.dart
index ca1057f..3bcdf44c 100644
--- a/tests/language_2/switch/fallthru_runtime_test.dart
+++ b/tests/language_2/switch/fallthru_runtime_test.dart
@@ -8,11 +8,6 @@
// BSD-style license that can be found in the LICENSE file.
// Check that FallThroughError is thrown if switch clause does not terminate.
-// VMOptions=
-// VMOptions=--force-switch-dispatch-type=0
-// VMOptions=--force-switch-dispatch-type=1
-// VMOptions=--force-switch-dispatch-type=2
-
import "package:expect/expect.dart";
String test(int n) {
diff --git a/tests/language_2/switch/infinite_switch_label_test.dart b/tests/language_2/switch/infinite_switch_label_test.dart
index 2ddfad6..82de828 100644
--- a/tests/language_2/switch/infinite_switch_label_test.dart
+++ b/tests/language_2/switch/infinite_switch_label_test.dart
@@ -6,11 +6,6 @@
// Test nested switch statement using labels.
-// VMOptions=
-// VMOptions=--force-switch-dispatch-type=0
-// VMOptions=--force-switch-dispatch-type=1
-// VMOptions=--force-switch-dispatch-type=2
-
library nested_switch_label;
import "package:expect/expect.dart";
diff --git a/tests/language_2/switch/label2_test.dart b/tests/language_2/switch/label2_test.dart
index 4c2dbb6..d018623 100644
--- a/tests/language_2/switch/label2_test.dart
+++ b/tests/language_2/switch/label2_test.dart
@@ -6,11 +6,6 @@
// Test switch statement using labels.
-// VMOptions=
-// VMOptions=--force-switch-dispatch-type=0
-// VMOptions=--force-switch-dispatch-type=1
-// VMOptions=--force-switch-dispatch-type=2
-
import 'package:expect/expect.dart';
void main() {
diff --git a/tests/language_2/switch/label_test.dart b/tests/language_2/switch/label_test.dart
index d67bd32..601330f 100644
--- a/tests/language_2/switch/label_test.dart
+++ b/tests/language_2/switch/label_test.dart
@@ -1,16 +1,10 @@
// Copyright (c) 2011, 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.
-
// Test switch statement using labels.
// @dart = 2.9
-// VMOptions=
-// VMOptions=--force-switch-dispatch-type=0
-// VMOptions=--force-switch-dispatch-type=1
-// VMOptions=--force-switch-dispatch-type=2
-
import "package:expect/expect.dart";
class Switcher {
diff --git a/tests/language_2/switch/nested_switch_label_test.dart b/tests/language_2/switch/nested_switch_label_test.dart
index 1d1b2b5..55b66b08 100644
--- a/tests/language_2/switch/nested_switch_label_test.dart
+++ b/tests/language_2/switch/nested_switch_label_test.dart
@@ -6,11 +6,6 @@
// Test nested switch statement using labels.
-// VMOptions=
-// VMOptions=--force-switch-dispatch-type=0
-// VMOptions=--force-switch-dispatch-type=1
-// VMOptions=--force-switch-dispatch-type=2
-
library nested_switch_label;
import "package:expect/expect.dart";
diff --git a/tests/language_2/switch/scope_test.dart b/tests/language_2/switch/scope_test.dart
index c36ec4a..c1b6536 100644
--- a/tests/language_2/switch/scope_test.dart
+++ b/tests/language_2/switch/scope_test.dart
@@ -1,16 +1,10 @@
// Copyright (c) 2011, 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.
-
// Test that a new scope is introduced for each switch case.
// @dart = 2.9
-// VMOptions=
-// VMOptions=--force-switch-dispatch-type=0
-// VMOptions=--force-switch-dispatch-type=1
-// VMOptions=--force-switch-dispatch-type=2
-
import "package:expect/expect.dart";
class SwitchScopeTest {
diff --git a/tests/language_2/switch/switch6_test.dart b/tests/language_2/switch/switch6_test.dart
index 9dad116..61e9196 100644
--- a/tests/language_2/switch/switch6_test.dart
+++ b/tests/language_2/switch/switch6_test.dart
@@ -1,14 +1,8 @@
// Copyright (c) 2011, 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.
-
// The break is in the right scope, http://b/3428700 was agreed upon.
-// VMOptions=
-// VMOptions=--force-switch-dispatch-type=0
-// VMOptions=--force-switch-dispatch-type=1
-// VMOptions=--force-switch-dispatch-type=2
-
// @dart = 2.9
import "package:expect/expect.dart";
diff --git a/tests/language_2/switch/switch_test.dart b/tests/language_2/switch/switch_test.dart
index f190806..52c131c 100644
--- a/tests/language_2/switch/switch_test.dart
+++ b/tests/language_2/switch/switch_test.dart
@@ -1,14 +1,8 @@
// Copyright (c) 2012, 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.
-
// Test switch statement.
-// VMOptions=
-// VMOptions=--force-switch-dispatch-type=0
-// VMOptions=--force-switch-dispatch-type=1
-// VMOptions=--force-switch-dispatch-type=2
-
// @dart = 2.9
import "package:expect/expect.dart";
diff --git a/tests/language_2/switch/try_catch_test.dart b/tests/language_2/switch/try_catch_test.dart
index d95e476..a37c890 100644
--- a/tests/language_2/switch/try_catch_test.dart
+++ b/tests/language_2/switch/try_catch_test.dart
@@ -7,11 +7,6 @@
// Regression test for issue 18869: Check that try-catch is working correctly
// inside switch-case clauses.
-// VMOptions=
-// VMOptions=--force-switch-dispatch-type=0
-// VMOptions=--force-switch-dispatch-type=1
-// VMOptions=--force-switch-dispatch-type=2
-
import "package:expect/expect.dart";
test_switch() {