blob: a7fcf98c2436416f8243cb3e7dcb74c1d472fe4a [file] [log] [blame]
// 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.
import 'dart:typed_data';
// This test checks that AOT compiler tries to keep loop blocks together and
// sinks cold code to the end of the function.
import 'package:expect/expect.dart';
import 'package:vm/testing/il_matchers.dart';
@pragma('vm:never-inline')
@pragma('vm:testing:print-flow-graph', 'ReorderBlocks')
int loop(Uint8List list) {
for (var i = 0; i < list.length; i++) {
if (list[i] == 1) {
return i;
}
}
return -1;
}
@pragma('vm:never-inline')
@pragma('vm:testing:print-flow-graph', 'ReorderBlocks')
int loop2(Uint8List list) {
int sum = 0;
for (var i = 0; i < list.length; i++) {
for (var j = 0; j < list.length; j++) {
if (list[j] == 1) {
break;
}
sum += list[j];
}
}
return sum;
}
@pragma('vm:never-inline')
@pragma('vm:testing:print-flow-graph', 'ReorderBlocks')
int bodyAlwaysThrows(Uint8List list) {
try {
throw 'Yay';
} catch (e) {
return list.length;
}
}
@pragma('vm:never-inline')
@pragma('vm:testing:print-flow-graph', 'ReorderBlocks')
int throwInALoop(Uint8List list) {
for (var i = 0; i < list.length; i++) {
if (list[i] == 1) {
return i;
}
if (list[i] == 2) {
throw 'Unexpected value';
}
}
return -1;
}
void main(List<String> args) {
final input = args.contains('foo')
? (Uint8List(10)..[5] = 1)
: (Uint8List(100)..[50] = 1);
Expect.equals(50, loop(input));
Expect.equals(0, loop2(input));
Expect.equals(100, bodyAlwaysThrows(input));
Expect.equals(50, throwInALoop(input));
}
void matchIL$loop(FlowGraph graph) {
graph.match(inCodegenBlockOrder: true, [
match.block('Graph'),
match.block('Function', [match.Goto('loop_header')]),
'loop_header' <<
match.block('Join', [match.Branch(match.any, ifTrue: 'loop_body')]),
'loop_body' <<
match.block('Target', [match.Branch(match.any, ifFalse: 'loop_inc')]),
'loop_inc' << match.block('Target', [match.Goto('loop_header')]),
]);
}
void matchIL$loop2(FlowGraph graph) {
graph.match(inCodegenBlockOrder: true, [
match.block('Graph'),
match.block('Function', [match.Goto('loop_header_1')]),
'loop_header_1' <<
match.block('Join', [match.Branch(match.any, ifTrue: 'loop_body_1')]),
'loop_body_1' << match.block('Target', [match.Goto('loop_header_2')]),
'loop_header_2' <<
match.block('Join', [
match.Branch(
match.any,
ifTrue: 'loop_body_2',
ifFalse: 'loop_body_2_exit_1',
),
]),
'loop_body_2' <<
match.block('Target', [
match.Branch(
match.any,
ifTrue: 'loop_body_2_exit_2',
ifFalse: 'loop_inc_2',
),
]),
'loop_inc_2' << match.block('Target', [match.Goto('loop_header_2')]),
'loop_body_2_exit_2' << match.block('Target', [match.Goto('loop_inc_1')]),
'loop_body_2_exit_1' << match.block('Target', [match.Goto('loop_inc_1')]),
'loop_inc_1' << match.block('Join', [match.Goto('loop_header_1')]),
]);
}
void matchIL$bodyAlwaysThrows(FlowGraph graph) {
graph.match([
match.block('Graph'),
match.block('Function'),
match.tryBlock(),
match.block('CatchBlock'),
match.block('Join'),
], inCodegenBlockOrder: true);
}
void matchIL$throwInALoop(FlowGraph graph) {
graph.match(inCodegenBlockOrder: true, [
match.block('Graph'),
match.block('Function', [match.Goto('loop_header')]),
'loop_header' <<
match.block('Join', [
'i' << match.Phi(match.any, 'inc_i'),
match.Branch(match.any, ifTrue: 'loop_body', ifFalse: 'return_fail'),
]),
'loop_body' <<
match.block('Target', [
match.Branch(
match.any,
ifTrue: 'return_found',
ifFalse: 'loop_body_cont',
),
]),
'loop_body_cont' <<
match.block('Target', [
match.Branch(match.any, ifTrue: 'throw', ifFalse: 'loop_inc'),
]),
'loop_inc' <<
match.block('Target', [
if (is32BitConfiguration) ...[
'i_32' << match.IntConverter('i', from: 'int64', to: 'int32'),
'inc_i_32' << match.BinaryInt32Op('i_32', match.any),
'inc_i' <<
match.IntConverter('inc_i_32', from: 'int32', to: 'int64'),
] else ...[
'inc_i' << match.BinaryInt64Op('i', match.any),
],
match.Goto('loop_header'),
]),
'return_found' << match.block('Target', [match.DartReturn('i')]),
'return_fail' << match.block('Target', [match.DartReturn(match.any)]),
'throw' << match.block('Target', [match.Throw(match.any)]),
]);
}