blob: faf771f384be7862306047cca558ccb9a9828320 [file] [log] [blame]
// Copyright (c) 2022, 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 'package:expect/expect.dart';
import 'package:vm/testing/il_matchers.dart';
// This test creates a phi which has multiple inputs referring to the same
// AllocateObject instruction. When delaying this allocation we need to
// look at all of these inputs and not just at the first one.
bool shouldPrint = false;
@pragma('vm:never-inline')
void blackhole(Object v) {
if (shouldPrint) {
print(v);
}
}
class X {
dynamic field;
@override
String toString() => 'X($field)';
}
// This function is used to create a phi with three arguments two of which
// point to the same definition: original value of [v].
@pragma('vm:prefer-inline')
X decisionTree(bool a, bool b, X v) {
if (a) {
v.field = 10;
blackhole(v);
return v;
} else if (b) {
return v;
} else {
return X();
}
}
@pragma('vm:never-inline')
@pragma('vm:testing:print-flow-graph')
dynamic testDelayAllocationsUnsunk(bool a, bool b) {
// Allocation is expected to be unsunk because no use dominates all other
// uses.
var v = X();
if (a) {
blackhole(b);
}
v = decisionTree(a, b, v);
blackhole(v);
return v.field;
}
@pragma('vm:never-inline')
@pragma('vm:testing:print-flow-graph')
dynamic testDelayAllocationsSunk(bool a, bool b) {
var v = X();
if (a) {
blackhole(b);
}
v.field = 42; // Allocation is expected to be sunk past if to this use.
v = decisionTree(a, b, v);
blackhole(v);
return v.field;
}
List<dynamic> testAllVariants(dynamic Function(bool, bool) f) {
return [
for (var a in [true, false])
for (var b in [true, false]) f(a, b),
];
}
void main(List<String> args) {
shouldPrint = args.contains("shouldPrint");
Expect.listEquals(
[10, 10, null, null], testAllVariants(testDelayAllocationsUnsunk));
Expect.listEquals(
[10, 10, 42, null], testAllVariants(testDelayAllocationsSunk));
}
void matchIL$testDelayAllocationsUnsunk(FlowGraph afterDelayAllocations) {
afterDelayAllocations.dump();
afterDelayAllocations.match([
match.block('Graph'),
match.block('Function', [
// Allocation must stay unsunk
match.AllocateObject()
])
]);
}
void matchIL$testDelayAllocationsSunk(FlowGraph afterDelayAllocations) {
afterDelayAllocations.dump();
afterDelayAllocations.match([
match.block('Graph'),
match.block('Function', [
// Allocation must be sunk from this block.
match.Branch(match.StrictCompare(match.any, match.any, kind: '==='),
ifTrue: 'B3', ifFalse: 'B4'),
]),
'B3' <<
match.block('Target', [
match.Goto('B5'),
]),
'B4' <<
match.block('Target', [
match.Goto('B5'),
]),
'B5' <<
match.block('Join', [
match.AllocateObject(),
]),
]);
}