// Copyright (c) 2023, 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 we don't leave in binary int operations that do not throw and
// have a calculated range of a single value, but instead replace the operation
// with that constant value.

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

@pragma('vm:never-inline')
@pragma('vm:testing:print-flow-graph')
int testUnsignedTruncatingDivision(Iterable i) => i.length ~/ 32;

void matchIL$testUnsignedTruncatingDivision(FlowGraph graph) {
  graph.match([
    match.block('Graph', [
      if (is32BitConfiguration) ...[
        'c5' << match.UnboxedConstant(value: 5, representation: 'int32'),
      ] else ...[
        'c32' << match.UnboxedConstant(value: 32, representation: 'int64'),
      ],
    ]),
    match.block('Function', [
      'it' << match.Parameter(index: 0),
      'len' << match.LoadField('it', slot: 'GrowableObjectArray.length'),
      if (is32BitConfiguration) ...[
        // 32-bit architectures don't handle 64-bit truncated division natively.
        // However, for powers of two, the runtime call
        //   m ~/ 2^n
        // gets replaced with
        //   (m + ((m >> 63) & (2^n - 1))) >> n
        // which works for both signed and unsigned values.
        //
        // In this specific case, with m = len and 2^n = 32,
        //   (len + ((len >> 63) & 31)) >> 5
        // However, the numerator len has a non-negative range since it's
        // retrieved from the length slot of an Iterable. This means (len >> 63)
        // is guaranteed to be 0, and the compiler should simplify this to
        //   len >> 5
        'unboxed_len' << match.UnboxInt32('len'),
        'retval_32' << match.BinaryInt32Op('unboxed_len', 'c5', op_kind: '>>'),
        'retval' << match.IntConverter('retval_32', from: 'int32', to: 'int64'),
      ] else ...[
        // 64-bit architectures do handle 64-bit truncated division natively,
        // so it's just a single operation there.
        'unboxed_len' << match.UnboxInt64('len'),
        'retval' << match.BinaryInt64Op('unboxed_len', 'c32', op_kind: '~/'),
      ],
      match.DartReturn('retval'),
    ]),
  ]);
}

void main(List<String> args) {
  final len = args.isEmpty ? 100 : int.parse(args.first);
  final list = List.generate(len, (i) => len - i);
  Expect.equals(len ~/ 32, testUnsignedTruncatingDivision(list));
}
