| // 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.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', [ |
| '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), |
| ]), |
| ]); |
| } |