| // 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)); |
| } |
| } |