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