Skip to content

Commit c886915

Browse files
rakudramaCommit Queue
authored andcommitted
[dart2js] Remove obsolete code from DCE
Remove an obsolete optimization: an instruction that has no users and has the only effect of checking the the receiver is non-null, can be removed when followed by another instruction with the same null-checking effect. With sound null safety, this rarely occurs. Most cases where this would have fired are now explicit null checks that are not dead because the second instruction is data-dependent of the check instruction. There is similar code in instruction selection to omit null checks, so we can rely on that. The remaining cases are with back-to-back JS-fragments. This can be managed by adding a null check. Added a test case for an added null check. Issue: #60327 Change-Id: Iace58bd944839166bc677d85bba2fe8d5c75da4c Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/446483 Reviewed-by: Mayank Patke <[email protected]> Commit-Queue: Stephen Adams <[email protected]>
1 parent 48bd8d5 commit c886915

File tree

2 files changed

+21
-60
lines changed

2 files changed

+21
-60
lines changed

pkg/compiler/lib/src/ssa/optimize.dart

Lines changed: 1 addition & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -3213,56 +3213,6 @@ class SsaDeadCodeEliminator extends HGraphVisitor implements OptimizationPhase {
32133213
closedWorld,
32143214
);
32153215

3216-
/// Determines whether we can delete [instruction] because the only thing it
3217-
/// does is throw the same exception as the next instruction that throws or
3218-
/// has an effect.
3219-
bool canFoldIntoFollowingInstruction(HInstruction instruction) {
3220-
assert(instruction.usedBy.isEmpty);
3221-
assert(instruction.canThrow(_abstractValueDomain));
3222-
3223-
if (!instruction.onlyThrowsNSM()) return false;
3224-
3225-
final receiver = instruction.getDartReceiver();
3226-
HInstruction? current = instruction.next;
3227-
do {
3228-
if ((current!.getDartReceiver() == receiver) &&
3229-
current.canThrow(_abstractValueDomain)) {
3230-
return true;
3231-
}
3232-
if (current is HForeignCode && current.isNullGuardFor(receiver)) {
3233-
return true;
3234-
}
3235-
if (current.canThrow(_abstractValueDomain) ||
3236-
current.sideEffects.hasSideEffects()) {
3237-
return false;
3238-
}
3239-
3240-
HInstruction? next = current.next;
3241-
if (next == null) {
3242-
// We do not merge blocks in our SSA graph, so if this block just jumps
3243-
// to a single successor, visit the successor, avoiding back-edges.
3244-
HBasicBlock? successor;
3245-
if (current is HGoto) {
3246-
successor = current.block!.successors.single;
3247-
} else if (current is HIf) {
3248-
// We also leave HIf nodes in place when one branch is dead.
3249-
HInstruction condition = current.inputs.first;
3250-
if (condition is HConstant) {
3251-
successor = condition.constant is TrueConstantValue
3252-
? current.thenBlock
3253-
: current.elseBlock;
3254-
assert(successor.isLive);
3255-
}
3256-
}
3257-
if (successor != null && successor.id > current.block!.id) {
3258-
next = successor.first;
3259-
}
3260-
}
3261-
current = next;
3262-
} while (current != null);
3263-
return false;
3264-
}
3265-
32663216
bool isTrivialDeadStoreReceiver(HInstruction instruction) {
32673217
// For an allocation, if all the loads are dead (awaiting removal after
32683218
// SsaLoadElimination) and the only other uses are stores, then the
@@ -3297,14 +3247,7 @@ class SsaDeadCodeEliminator extends HGraphVisitor implements OptimizationPhase {
32973247
if (isTrivialDeadStore(instruction)) return true;
32983248
if (instruction.allowDCE) return true;
32993249
if (instruction.sideEffects.hasSideEffects()) return false;
3300-
if (instruction.canThrow(_abstractValueDomain)) {
3301-
if (canFoldIntoFollowingInstruction(instruction)) {
3302-
// TODO(35996): If we remove [instruction], the source location of the
3303-
// 'equivalent' instruction should be updated.
3304-
return true;
3305-
}
3306-
return false;
3307-
}
3250+
if (instruction.canThrow(_abstractValueDomain)) return false;
33083251
if (instruction is HParameterValue) return false;
33093252
if (instruction is HLocalSet) return false;
33103253
return true;

pkg/compiler/test/js/js_spec_optimization_test.dart

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,8 @@ const String TEST_3 = r"""
5353
'#.toUpperCase()', s);
5454
print(s2);
5555
56-
// absent: 'toLowerCase' - removed since s.toUpperCase() generates the same
57-
// noSuchMethod.
56+
// present: 'toLowerCase'
57+
// present: 'toUpperCase'
5858
}
5959
""";
6060

@@ -85,6 +85,23 @@ const String TEST_5 = r"""
8585
}
8686
""";
8787

88+
const String TEST_6 = r"""
89+
import 'dart:_foreign_helper';
90+
main() {
91+
var s = JS('String|Null', '"Hello"');
92+
s!;
93+
var s1 = JS('returns:String;depends:none;effects:none;throws:null(1)',
94+
'#.toLowerCase()', s);
95+
var s2 = JS('returns:String;depends:none;effects:none;throws:null(1)',
96+
'#.toUpperCase()', s);
97+
print(s2);
98+
// absent: 'toLowerCase' - removed since has no effect after null check.
99+
// present: 'toUpperCase' - retained since used.
100+
// present: '"Hello".toUpperCase' - null check folded into call.
101+
// absent: '.toString;' - no null check as folded into 's.toUpperCase()'
102+
}
103+
""";
104+
88105
main() {
89106
runTests() async {
90107
check(String test) async {
@@ -112,6 +129,7 @@ main() {
112129
await check(TEST_3);
113130
await check(TEST_4);
114131
await check(TEST_5);
132+
await check(TEST_6);
115133
}
116134

117135
asyncTest(() async {

0 commit comments

Comments
 (0)