[dart2js] Replace phi with controlling condition

Rewrite `phi(true, false)` to the controlling condition.

This patchset comparison shows the general effect: https://dart-review.googlesource.com/c/sdk/+/340065/2..3/pkg/compiler/test/codegen/data/phi_to_condition_test.dart

This change is a partial remedy for http://dartbug.com/54115

Issue: #54115
Change-Id: Ibcc5d17f6c4c8ad9600840ec106f84edcd008e4a
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/340065
Reviewed-by: Nate Biggs <natebiggs@google.com>
Commit-Queue: Stephen Adams <sra@google.com>
This commit is contained in:
Stephen Adams 2024-01-25 01:11:03 +00:00 committed by Commit Queue
parent e00ab8c3ce
commit 75b1973041
3 changed files with 105 additions and 24 deletions

View file

@ -333,13 +333,64 @@ class SsaInstructionSimplifier extends HBaseVisitor<HInstruction>
}
// Simplify some CFG diamonds to equivalent expressions.
simplifyPhis(HBasicBlock block) {
// Is [block] the join point for a simple diamond that generates a single
// phi node?
if (block.phis.isEmpty) return;
HPhi phi = block.phis.first as HPhi;
if (phi.next != null) return;
void simplifyPhis(HBasicBlock block) {
if (block.predecessors.length != 2) return;
// Do 'statement' simplifications first, as they might reduce the number of
// phis to one, enabling an 'expression' simplification.
HInstruction? phi = block.phis.first;
while (phi != null) {
final next = phi.next;
simplifyStatementPhi(block, phi as HPhi);
phi = next;
}
phi = block.phis.first;
if (phi != null && phi.next == null) {
simplifyExpressionPhi(block, phi as HPhi);
}
}
/// Simplify a single phi when there are possibly other phis (i.e. the result
/// might not be an expression).
void simplifyStatementPhi(HBasicBlock block, HPhi phi) {
HBasicBlock dominator = block.dominator!;
// Extract the controlling condition.
final controlFlow = dominator.last;
if (controlFlow is! HIf) return;
HInstruction condition = controlFlow.inputs.single;
if (condition.isBoolean(_abstractValueDomain).isPotentiallyFalse) return;
// condition ? true : false --> condition
// condition ? condition : false --> condition
// condition ? true : condition --> condition
final left = phi.inputs[0];
final right = phi.inputs[1];
if ((_isBoolConstant(left, true) || left == condition) &&
(_isBoolConstant(right, false) || right == condition)) {
block.rewrite(phi, condition);
block.removePhi(phi);
condition.sourceElement ??= phi.sourceElement;
return;
}
// condition ? false : true --> !condition
if (_isBoolConstant(left, false) && _isBoolConstant(right, true)) {
HInstruction replacement = HNot(condition, _abstractValueDomain.boolType)
..sourceElement = phi.sourceElement
..sourceInformation = phi.sourceInformation;
block.addAtEntry(replacement);
block.rewrite(phi, replacement);
block.removePhi(phi);
return;
}
}
/// Simplify some CFG diamonds to equivalent expressions.
void simplifyExpressionPhi(HBasicBlock block, HPhi phi) {
// Is [block] the join point for a simple diamond?
assert(phi.inputs.length == 2);
HBasicBlock b1 = block.predecessors[0];
HBasicBlock b2 = block.predecessors[1];
@ -350,6 +401,7 @@ class SsaInstructionSimplifier extends HBaseVisitor<HInstruction>
final controlFlow = dominator.last;
if (controlFlow is! HIf) return;
HInstruction test = controlFlow.inputs.single;
if (test.usedBy.length > 1) return;
bool negated = false;
@ -396,7 +448,7 @@ class SsaInstructionSimplifier extends HBaseVisitor<HInstruction>
block.removePhi(phi);
return;
}
// If 'x'is nullable boolean,
// If 'x' is nullable boolean,
//
// x == null ? true : x ---> !(x == false)
//

View file

@ -59,6 +59,20 @@ int noopOr(int a) {
return (a == 1 || a == 3 || a == 5 || a == 7 || a == 9) ? 100 : 100;
}
/*member: constantFoldedControlFlow3:function(a) {
return a;
}*/
bool constantFoldedControlFlow3(bool a) {
return a && 1 == 1 && 2 == 2;
}
/*member: constantFoldedControlFlow4:function(a) {
return a;
}*/
bool constantFoldedControlFlow4(bool a) {
return 1 == 1 && a && 2 == 2;
}
// Problem cases.
//
// Move the following cases above this comment when the code quality improves.
@ -130,29 +144,15 @@ bool constantFoldedControlFlow1(bool a, bool b) {
}
/*member: constantFoldedControlFlow2:function(a, b) {
return a && b && true;
}*/
bool constantFoldedControlFlow2(bool a, bool b) {
return 1 == 1 && a && 2 == 2 && b && 3 == 3;
}
/*member: constantFoldedControlFlow3:function(a) {
var t1;
if (a)
t1 = true;
t1 = b;
else
t1 = false;
return t1;
}*/
bool constantFoldedControlFlow3(bool a) {
return a && 1 == 1 && 2 == 2;
}
/*member: constantFoldedControlFlow4:function(a) {
return a && true;
}*/
bool constantFoldedControlFlow4(bool a) {
return 1 == 1 && a && 2 == 2;
bool constantFoldedControlFlow2(bool a, bool b) {
return 1 == 1 && a && 2 == 2 && b && 3 == 3;
}
@pragma('dart2js:disable-inlining')

View file

@ -0,0 +1,29 @@
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
@pragma('dart2js:never-inline')
/*member: foo1:function(a, b) {
var changed = a !== b;
if (changed)
A.log("changed");
return changed;
}*/
foo1(int a, int b) {
bool changed = false;
if (a != b) {
changed = true;
log('changed');
}
return changed;
}
@pragma('dart2js:never-inline')
/*member: log:ignore*/
void log(String s) {}
/*member: main:ignore*/
main() {
foo1(1, 2);
foo1(2, 1);
}