// 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.

// Verify that List bound check is eliminated in a simple for-loop
// over growable list.

import 'package:vm/testing/il_matchers.dart';

@pragma('vm:never-inline')
void myprint(Object o) {
  print(o);
}

@pragma('vm:never-inline')
@pragma('vm:testing:print-flow-graph')
useList(List<int> a) {
  for (int i = 0; i < a.length; ++i) {
    final value = a[i];
    myprint(value);
  }
}

main() {
  useList([]);
  useList([100]);
}

void matchIL$useList(FlowGraph graph) {
  graph.dump();
  graph.match([
    match.block('Graph', [
      'c_null' << match.Constant(value: null),
      'c_zero' << match.UnboxedConstant(value: 0),
      'c_one' << match.UnboxedConstant(value: 1),
    ]),
    match.block('Function', [
      'a' << match.Parameter(index: 0),
      match.CheckStackOverflow(),
      match.Goto('B5'),
    ]),
    'B5' <<
        match.block('Join', [
          'i' << match.Phi('c_zero', 'i+1'),
          match.CheckStackOverflow(),
          'a.length' <<
              match.LoadField('a', slot: 'GrowableObjectArray.length'),
          if (is32BitConfiguration) ...[
            'i_32a' << match.IntConverter('i', from: 'int64', to: 'int32'),
            'a.length_unboxed' << match.UnboxInt32('a.length'),
          ] else ...[
            'a.length_unboxed' << match.UnboxInt64('a.length'),
          ],
          match.Branch(
            match.RelationalOp(
              is32BitConfiguration ? 'i_32a' : 'i',
              'a.length_unboxed',
              kind: '<',
            ),
            ifTrue: 'B3',
            ifFalse: 'B4',
          ),
        ]),
    'B3' <<
        match.block('Target', [
          // No bounds check here.
          if (is32BitConfiguration)
            'i_boxed' << match.BoxInt64('i', skipUntilMatched: false),
          'a.data' <<
              match.LoadField(
                'a',
                slot: 'GrowableObjectArray.data',
                skipUntilMatched: false,
              ),
          'value' <<
              match.LoadIndexed(
                'a.data',
                is32BitConfiguration ? 'i_boxed' : 'i',
                skipUntilMatched: false,
              ),
          'value_unboxed' << match.UnboxInt64('value'),
          match.StaticCall('value_unboxed'),
          if (is32BitConfiguration) ...[
            'i_32' << match.IntConverter('i', from: 'int64', to: 'int32'),
            'i+1_32' << match.BinaryInt32Op('i_32', 'c_one', op_kind: '+'),
            'i+1' << match.IntConverter('i+1_32', from: 'int32', to: 'int64'),
          ] else ...[
            'i+1' << match.BinaryInt64Op('i', 'c_one', op_kind: '+'),
          ],
          match.Goto('B5'),
        ]),
    'B4' << match.block('Target', [match.DartReturn('c_null')]),
  ]);
}
