blob: c7f1c58826e5f48b8d4553a190408f141ba0a585 [file] [log] [blame]
// Copyright (c) 2023, 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.
// Given
//
// Branch if StrictCompare(v === Constant(C)) then B1 else B2
//
// constant propagation should treat all uses of `v` dominated by
// B1 as if they were equal to C.
//
// The same applies to the negated case, where `v` is equal to `C`
// in the false successor. The same applies to EqualityCompare() when applied
// to integers.
//
// Note that we don't want to actually eagerly replace `v` with `C` because
// it might complicate subsequent optimizations by introducing redundant phis.
import 'package:vm/testing/il_matchers.dart';
class A {
final int v;
const A(this.v);
}
@pragma('vm:never-inline')
@pragma('vm:testing:print-flow-graph')
int strictCompareValueEqConstant(A value) {
if (value == const A(0)) {
return value.v;
} else {
return 42;
}
}
void matchIL$strictCompareValueEqConstant(FlowGraph graph) {
graph.match([
match.block('Graph', [
'A(0)' << match.Constant(value: "Instance of A"),
'int 0' << match.UnboxedConstant(value: 0),
'int 42' << match.UnboxedConstant(value: 42),
]),
match.block('Function', [
'value' << match.Parameter(index: 0),
match.Branch(
match.StrictCompare('value', 'A(0)', kind: '==='),
ifTrue: 'B1',
ifFalse: 'B2',
),
]),
'B1' << match.block('Target', [match.DartReturn('int 0')]),
'B2' << match.block('Target', [match.DartReturn('int 42')]),
]);
}
@pragma('vm:never-inline')
@pragma('vm:testing:print-flow-graph')
int strictCompareConstantEqValue(A value) {
if (const A(0) == value) {
return value.v;
} else {
return 42;
}
}
void matchIL$strictCompareConstantEqValue(FlowGraph graph) {
graph.match([
match.block('Graph', [
'A(0)' << match.Constant(value: "Instance of A"),
'int 0' << match.UnboxedConstant(value: 0),
'int 42' << match.UnboxedConstant(value: 42),
]),
match.block('Function', [
'value' << match.Parameter(index: 0),
match.Branch(
match.StrictCompare('value', 'A(0)', kind: '==='),
ifTrue: 'B1',
ifFalse: 'B2',
),
]),
'B1' << match.block('Target', [match.DartReturn('int 0')]),
'B2' << match.block('Target', [match.DartReturn('int 42')]),
]);
}
@pragma('vm:never-inline')
@pragma('vm:testing:print-flow-graph')
int strictCompareValueNeConstant(A value) {
if (value != const A(0)) {
return 42;
} else {
return value.v;
}
}
void matchIL$strictCompareValueNeConstant(FlowGraph graph) {
graph.match([
match.block('Graph', [
'A(0)' << match.Constant(value: "Instance of A"),
'int 0' << match.UnboxedConstant(value: 0),
'int 42' << match.UnboxedConstant(value: 42),
]),
match.block('Function', [
'value' << match.Parameter(index: 0),
match.Branch(
match.StrictCompare('value', 'A(0)', kind: '!=='),
ifTrue: 'B1',
ifFalse: 'B2',
),
]),
'B1' << match.block('Target', [match.DartReturn('int 42')]),
'B2' << match.block('Target', [match.DartReturn('int 0')]),
]);
}
@pragma('vm:never-inline')
@pragma('vm:testing:print-flow-graph')
int strictCompareConstantNeValue(A value) {
if (const A(0) != value) {
return 42;
} else {
return value.v;
}
}
void matchIL$strictCompareConstantNeValue(FlowGraph graph) {
graph.match([
match.block('Graph', [
'A(0)' << match.Constant(value: "Instance of A"),
'int 0' << match.UnboxedConstant(value: 0),
'int 42' << match.UnboxedConstant(value: 42),
]),
match.block('Function', [
'value' << match.Parameter(index: 0),
match.Branch(
match.StrictCompare('value', 'A(0)', kind: '!=='),
ifTrue: 'B1',
ifFalse: 'B2',
),
]),
'B1' << match.block('Target', [match.DartReturn('int 42')]),
'B2' << match.block('Target', [match.DartReturn('int 0')]),
]);
}
@pragma('vm:never-inline')
@pragma('vm:testing:print-flow-graph')
bool strictCompareBoolEqTrue(bool value) {
// Note: expect false to be propagated into the false successor as well.
if (value == true) {
return !value;
} else {
return !value;
}
}
void matchIL$strictCompareBoolEqTrue(FlowGraph graph) {
graph.match([
match.block('Graph', [
'true' << match.Constant(value: true),
'false' << match.Constant(value: false),
]),
match.block('Function', [
'value' << match.Parameter(index: 0),
match.Branch(
match.StrictCompare('value', 'true', kind: '==='),
ifTrue: 'B1',
ifFalse: 'B2',
),
]),
'B1' << match.block('Target', [match.DartReturn('false')]),
'B2' << match.block('Target', [match.DartReturn('true')]),
]);
}
@pragma('vm:never-inline')
@pragma('vm:testing:print-flow-graph')
bool strictCompareBoolNeTrue(bool value) {
// Note: expect false to be propagated into the true successor as well.
if (value != true) {
return !value;
} else {
return !value;
}
}
void matchIL$strictCompareBoolNeTrue(FlowGraph graph) {
graph.match([
match.block('Graph', [
'true' << match.Constant(value: true),
'false' << match.Constant(value: false),
]),
match.block('Function', [
'value' << match.Parameter(index: 0),
match.Branch(
match.StrictCompare('value', 'true', kind: '!=='),
ifTrue: 'B1',
ifFalse: 'B2',
),
]),
'B1' << match.block('Target', [match.DartReturn('true')]),
'B2' << match.block('Target', [match.DartReturn('false')]),
]);
}
@pragma('vm:never-inline')
@pragma('vm:testing:print-flow-graph')
int equalityCompareValueEqConstant(int value) {
if (value == 0) {
return value + 1;
} else {
return 42;
}
}
void matchIL$equalityCompareValueEqConstant(FlowGraph graph) {
graph.match([
match.block('Graph', [
'int 1' << match.UnboxedConstant(value: 1),
'int 0' << match.UnboxedConstant(value: 0),
'int 42' << match.UnboxedConstant(value: 42),
]),
match.block('Function', [
'value' << match.Parameter(index: 0),
match.Branch(
match.EqualityCompare('value', 'int 0', kind: '=='),
ifTrue: 'B1',
ifFalse: 'B2',
),
]),
'B1' << match.block('Target', [match.DartReturn('int 1')]),
'B2' << match.block('Target', [match.DartReturn('int 42')]),
]);
}
@pragma('vm:never-inline')
@pragma('vm:testing:print-flow-graph')
int foldingOfRepeatedComparison(int value) {
final v = value >= 1;
if (v) {
if (v) {
return 1;
} else {
return 24;
}
} else {
return 42;
}
}
void matchIL$foldingOfRepeatedComparison(FlowGraph graph) {
graph.match([
match.block('Graph', [
'int 1' << match.UnboxedConstant(value: 1),
'int 42' << match.UnboxedConstant(value: 42),
]),
match.block('Function', [
'value' << match.Parameter(index: 0),
match.Branch(
match.RelationalOp('value', 'int 1', kind: '>='),
ifTrue: 'B1',
ifFalse: 'B2',
),
]),
'B1' << match.block('Target', [match.DartReturn('int 1')]),
'B2' << match.block('Target', [match.DartReturn('int 42')]),
]);
}
void main(List<String> args) {
for (var v in [
A(0),
A(1),
A(42),
A(int.parse(args.isEmpty ? "24" : args[0])),
]) {
print(strictCompareValueEqConstant(v));
print(strictCompareConstantEqValue(v));
print(strictCompareValueNeConstant(v));
print(strictCompareConstantNeValue(v));
print(equalityCompareValueEqConstant(v.v));
print(foldingOfRepeatedComparison(v.v));
}
for (var v in [true, false]) {
print(strictCompareBoolEqTrue(v));
print(strictCompareBoolNeTrue(v));
}
}