3541a4a125
The single-entry / single-exit patterns of the CFG prevent inlined throw statements and throw expressions from having exit edges like a statement-level throw. If the join block is not reachable on one path of a diamond, the condition can be substituted as if the join is on the other path. Change-Id: I9cc1058b4ca0cab892db7f1197141c5157d2bc5b Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/180724 Commit-Queue: Stephen Adams <sra@google.com> Reviewed-by: Mayank Patke <fishythefish@google.com>
216 lines
4.6 KiB
Dart
216 lines
4.6 KiB
Dart
// Copyright (c) 2021, 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.7
|
|
|
|
// Tests for simplifying that we know a condition is true in the then-branch and
|
|
// false in the else-branch, and sometimes after join when a diamond exits.
|
|
|
|
/*member: check0:ConditionValue=[]*/
|
|
check0(int x) {
|
|
if (x != 1) {
|
|
return 200;
|
|
} else {
|
|
return 400;
|
|
}
|
|
}
|
|
|
|
/*member: check1:ConditionValue=[count=1&value=false&where=else,count=1&value=true&where=then]*/
|
|
check1(int x) {
|
|
if (x == 1) {
|
|
if (x == 1) return 100;
|
|
return 200;
|
|
} else {
|
|
if (x == 1) return 300;
|
|
return 400;
|
|
}
|
|
}
|
|
|
|
/*member: check2:ConditionValue=[count=1&value=false&where=then,count=1&value=true&where=else]*/
|
|
check2(int x) {
|
|
if (x != 1) {
|
|
if (x == 1) return 100;
|
|
return 200;
|
|
} else {
|
|
if (x == 1) return 300;
|
|
return 400;
|
|
}
|
|
}
|
|
|
|
/*member: check3:ConditionValue=[count=1&value=false&where=else,count=1&value=true&where=then]*/
|
|
check3(int x) {
|
|
if (x == 1) {
|
|
if (x != 1) return 100;
|
|
return 200;
|
|
} else {
|
|
if (x != 1) return 300;
|
|
return 400;
|
|
}
|
|
}
|
|
|
|
/*member: check4:ConditionValue=[count=1&value=false&where=else,count=1&value=true&where=then]*/
|
|
check4(int x) {
|
|
if (x != 1) {
|
|
if (x != 1) return 100;
|
|
return 200;
|
|
} else {
|
|
if (x != 1) return 300;
|
|
return 400;
|
|
}
|
|
}
|
|
|
|
// The first throw is in statement position, so it has an exit edge, leaving the
|
|
// rest of the method in the first branch 'else'.
|
|
/*member: join0Else:ConditionValue=[count=2&value=false&where=else]*/
|
|
join0Else(int x) {
|
|
int a = 0, b = 0, c = 0;
|
|
if (x == 1)
|
|
throw 'bad';
|
|
else
|
|
a = x + 1;
|
|
if (x == 1)
|
|
throw 'bad';
|
|
else
|
|
b = x + 2;
|
|
if (x == 1)
|
|
throw 'bad';
|
|
else
|
|
c = x + 3;
|
|
return a * b * c;
|
|
}
|
|
|
|
/*member: join1:ConditionValue=[count=2&value=false&where=else-join]*/
|
|
join1(int x) {
|
|
int a = (x == 1 ? throw 'bad' : x) + 1;
|
|
int b = (x == 1 ? throw 'bad' : x) + 2;
|
|
int c = (x == 1 ? throw 'bad' : x) + 3;
|
|
return a * b * c;
|
|
}
|
|
|
|
/*member: join2:ConditionValue=[count=2&value=true&where=then-join]*/
|
|
join2(int x) {
|
|
int a = (x == 1 ? x : throw 'bad') + 1;
|
|
int b = (x == 1 ? x : throw 'bad') + 2;
|
|
int c = (x == 1 ? x : throw 'bad') + 3;
|
|
return a * b * c;
|
|
}
|
|
|
|
/*member: join3:ConditionValue=[count=2&value=false&where=else-join]*/
|
|
join3(int x) {
|
|
int a = (x != 1 ? throw 'bad' : x) + 1;
|
|
int b = (x != 1 ? throw 'bad' : x) + 2;
|
|
int c = (x != 1 ? throw 'bad' : x) + 3;
|
|
return a * b * c;
|
|
}
|
|
|
|
/*member: join4:ConditionValue=[count=2&value=true&where=then-join]*/
|
|
join4(int x) {
|
|
int a = (x != 1 ? x : throw 'bad') + 1;
|
|
int b = (x != 1 ? x : throw 'bad') + 2;
|
|
int c = (x != 1 ? x : throw 'bad') + 3;
|
|
return a * b * c;
|
|
}
|
|
|
|
/*member: loop1:ConditionValue=[count=1&value=true&where=then]*/
|
|
loop1(int x) {
|
|
for (int i = 0; i < 10; i++) {
|
|
if (x == 1) {
|
|
sink = x == 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*member: loop2HoistedThen:ConditionValue=[count=1&value=false&where=hoisted-then]*/
|
|
loop2HoistedThen(int x) {
|
|
// t1 = x == 1;
|
|
// t2 = !t1;
|
|
// loop:
|
|
// if (t1)
|
|
// sink = t2; // replaced with `false`.
|
|
for (int i = 0; i < 10; i++) {
|
|
if (x == 1) {
|
|
sink = x != 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*member: loop3:ConditionValue=[count=1&value=false&where=then]*/
|
|
loop3(int x) {
|
|
for (int i = 0; i < 10; i++) {
|
|
if (x != 1) {
|
|
sink = x == 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*member: loop4:ConditionValue=[count=1&value=true&where=then]*/
|
|
loop4(int x) {
|
|
for (int i = 0; i < 10; i++) {
|
|
if (x != 1) {
|
|
sink = x != 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*member: loop5HoistedElseJoin:ConditionValue=[count=1&value=true&where=hoisted-else-join]*/
|
|
loop5HoistedElseJoin(int x) {
|
|
for (int i = 0; i < 10; i++) {
|
|
sink = (x == 1 ? throw 'bad' : x) + 1;
|
|
sink = x != 1;
|
|
}
|
|
}
|
|
|
|
// Unlike loop5, this is not 'hoisted'. GVN is not required to match the
|
|
// condition with its use, so the subsitution happens in a simplify pass before
|
|
// GVN/LICM can hoist the negation.
|
|
/*member: loop6ElseJoin:ConditionValue=[count=1&value=false&where=else-join]*/
|
|
loop6ElseJoin(bool x) {
|
|
for (int i = 0; i < 10; i++) {
|
|
sink = (x ? throw 'bad' : i) + 1;
|
|
sink = !x;
|
|
}
|
|
}
|
|
|
|
dynamic sink;
|
|
|
|
void main() {
|
|
check0(1);
|
|
check0(2);
|
|
|
|
check1(1);
|
|
check1(2);
|
|
check2(1);
|
|
check2(2);
|
|
check3(1);
|
|
check3(2);
|
|
check4(1);
|
|
check4(2);
|
|
|
|
join0Else(1);
|
|
join0Else(2);
|
|
join1(1);
|
|
join1(2);
|
|
join2(1);
|
|
join2(2);
|
|
join3(1);
|
|
join3(2);
|
|
join4(1);
|
|
join4(2);
|
|
|
|
loop1(1);
|
|
loop1(2);
|
|
loop2HoistedThen(1);
|
|
loop2HoistedThen(2);
|
|
loop3(1);
|
|
loop3(2);
|
|
loop4(1);
|
|
loop4(2);
|
|
loop5HoistedElseJoin(1);
|
|
loop5HoistedElseJoin(2);
|
|
loop6ElseJoin(true);
|
|
loop6ElseJoin(false);
|
|
|
|
print(sink);
|
|
}
|