mirror of
https://github.com/dart-lang/sdk
synced 2024-09-05 08:24:39 +00:00
[ddc] Avoid emitting dead branches in conditionals
Don't emit dead code branches that are guarded by boolean literals. Add a single level of simplification for !true -> false, and !false -> true. While this might not be very common in code, it does allow for a useful pattern in the runtime libraries. Branches can be included into the compiled code or not based on a compile time flag. Change-Id: Ib90e1e951cea3ef8c75b944635776b292759594a Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/243363 Commit-Queue: Nicholas Shahan <nshahan@google.com> Reviewed-by: Anna Gringauze <annagrin@google.com>
This commit is contained in:
parent
c27086d567
commit
651350bc3b
|
@ -4396,11 +4396,32 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
|
|||
@override
|
||||
js_ast.Statement visitIfStatement(IfStatement node) {
|
||||
var condition = _visitTest(node.condition);
|
||||
var then = _visitScope(node.then);
|
||||
if (node.otherwise != null) {
|
||||
return js_ast.If(condition, then, _visitScope(node.otherwise));
|
||||
if (condition is js_ast.LiteralBool) {
|
||||
// Avoid emitting the branch with code that will never execute.
|
||||
if (condition.value) {
|
||||
return _visitScope(node.then).toStatement();
|
||||
} else {
|
||||
return _visitScope(node.otherwise).toStatement();
|
||||
}
|
||||
}
|
||||
return js_ast.If(
|
||||
condition, _visitScope(node.then), _visitScope(node.otherwise));
|
||||
}
|
||||
return js_ast.If.noElse(condition, then);
|
||||
|
||||
if (condition is js_ast.LiteralBool) {
|
||||
if (condition.value) {
|
||||
// Avoid emitting conditional when it is always true.
|
||||
// ex: `if (true) {abc...}` -> `{abc...}`
|
||||
return _visitScope(node.then).toStatement();
|
||||
} else {
|
||||
// Avoid emitting conditional and then when it will never execute.
|
||||
// ex: `if (false) {abc...}` -> `;`
|
||||
return js_ast.EmptyStatement();
|
||||
}
|
||||
}
|
||||
|
||||
return js_ast.If.noElse(condition, _visitScope(node.then));
|
||||
}
|
||||
|
||||
/// Visits a statement, and ensures the resulting AST handles block scope
|
||||
|
@ -5905,11 +5926,19 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
|
|||
negated: true);
|
||||
}
|
||||
|
||||
var jsOperand = _visitTest(operand);
|
||||
if (jsOperand is js_ast.LiteralBool) {
|
||||
// Flipping the value here for `!true` or `!false` allows for simpler
|
||||
// `if (true)` or `if (false)` detection and optimization.
|
||||
return js_ast.LiteralBool(!jsOperand.value)
|
||||
.withSourceInformation(jsOperand.sourceInformation)
|
||||
as js_ast.LiteralBool;
|
||||
}
|
||||
|
||||
// Logical negation, `!e`, is a boolean conversion context since it is
|
||||
// defined as `e ? false : true`.
|
||||
return js
|
||||
.call('!#', _visitTest(operand))
|
||||
.withSourceInformation(continueSourceMap) as js_ast.Expression;
|
||||
return js.call('!#', jsOperand).withSourceInformation(continueSourceMap)
|
||||
as js_ast.Expression;
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -5930,6 +5959,16 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
|
|||
@override
|
||||
js_ast.Expression visitConditionalExpression(ConditionalExpression node) {
|
||||
var condition = _visitTest(node.condition);
|
||||
if (condition is js_ast.LiteralBool) {
|
||||
if (condition.value) {
|
||||
// Avoid emitting conditional when one branch is effectively dead code.
|
||||
// ex: `true ? foo : bar` -> `foo`
|
||||
return _visitExpression(node.then);
|
||||
} else {
|
||||
// ex: `false ? foo : bar` -> `bar`
|
||||
return _visitExpression(node.otherwise);
|
||||
}
|
||||
}
|
||||
var then = _visitExpression(node.then);
|
||||
var otherwise = _visitExpression(node.otherwise);
|
||||
return js.call('# ? # : #', [condition, then, otherwise])
|
||||
|
|
54
tests/dartdevc/if_else_literal_compilation_test.dart
Normal file
54
tests/dartdevc/if_else_literal_compilation_test.dart
Normal file
|
@ -0,0 +1,54 @@
|
|||
// Copyright (c) 2019, 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.
|
||||
|
||||
import 'dart:_foreign_helper' show JS;
|
||||
|
||||
import 'package:expect/expect.dart';
|
||||
|
||||
void main() async {
|
||||
var count = 0;
|
||||
if (JS<bool>('!', 'false')) {
|
||||
// Should be eliminated from the output based on the condition above.
|
||||
JS('', 'syntax error here!');
|
||||
}
|
||||
count++;
|
||||
if (JS<bool>('!', 'true')) {
|
||||
count++;
|
||||
} else {
|
||||
// Should be eliminated from the output based on the condition above.
|
||||
JS('', 'syntax error here!');
|
||||
}
|
||||
if (JS<bool>('!', 'false')) {
|
||||
// Should be eliminated from the output based on the condition above.
|
||||
JS('', 'syntax error here!');
|
||||
} else {
|
||||
count++;
|
||||
}
|
||||
if (!JS<bool>('!', 'true')) {
|
||||
// Should be eliminated from the output based on the condition above.
|
||||
JS('', 'syntax error here!');
|
||||
}
|
||||
count++;
|
||||
if (!JS<bool>('!', 'false')) {
|
||||
count++;
|
||||
} else {
|
||||
// Should be eliminated from the output based on the condition above.
|
||||
JS('', 'syntax error here!');
|
||||
}
|
||||
if (!JS<bool>('!', 'true')) {
|
||||
// Should be eliminated from the output based on the condition above.
|
||||
JS('', 'syntax error here!');
|
||||
} else {
|
||||
count++;
|
||||
}
|
||||
|
||||
JS<bool>('!', 'true') ? count++ : JS('', 'syntax error here!');
|
||||
JS<bool>('!', 'false') ? JS('', 'syntax error here!') : count++;
|
||||
!JS<bool>('!', 'true') ? JS('', 'syntax error here!') : count++;
|
||||
!JS<bool>('!', 'false') ? count++ : JS('', 'syntax error here!');
|
||||
|
||||
// All expected branches are evaluated, and none of the syntax errors where
|
||||
// compiled at all.
|
||||
Expect.equals(10, count);
|
||||
}
|
Loading…
Reference in a new issue