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

// Test that multiple comparisons involving the same uint32 value
// with different constants do not generate multiple IntConverter
// instructions, only a sequence of 'Branch(EqualityCompare(...))'.
// Regression test for https://github.com/dart-lang/sdk/issues/56839.

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

class Token {
  Token(TokenType type, int offset)
    : typeAndOffset = ((offset << 8) | type.index);

  int typeAndOffset;

  int get typeIndex => typeAndOffset & 0xff;
}

class TokenType {
  static const TokenType T10 = const TokenType(10, 'T10');
  static const TokenType T20 = const TokenType(20, 'T20');
  static const TokenType T30 = const TokenType(30, 'T30');
  static const TokenType T40 = const TokenType(40, 'T40');
  static const TokenType T50 = const TokenType(50, 'T50');
  static const TokenType T60 = const TokenType(50, 'T60');
  static const TokenType T70 = const TokenType(50, 'T70');

  const TokenType(this.index, this.name);

  final int index;
  final String name;
}

extension TokenIsAExtension on Token {
  @pragma("vm:prefer-inline")
  bool isA(TokenType value) {
    return value.index == typeIndex;
  }
}

@pragma('vm:never-inline')
@pragma('vm:testing:print-flow-graph')
bool looksLikeExpressionStart(Token next) =>
    next.isA(TokenType.T10) ||
    next.isA(TokenType.T20) ||
    next.isA(TokenType.T30) ||
    next.isA(TokenType.T40) ||
    next.isA(TokenType.T50);

void matchIL$looksLikeExpressionStart(FlowGraph graph) {
  graph.match([
    match.block('Graph', [
      'int 255' << match.UnboxedConstant(value: 255),
      'int 10' << match.UnboxedConstant(value: 10),
      'int 20' << match.UnboxedConstant(value: 20),
      'int 30' << match.UnboxedConstant(value: 30),
      'int 40' << match.UnboxedConstant(value: 40),
      'int 50' << match.UnboxedConstant(value: 50),
    ]),
    match.block('Function', [
      'value' << match.Parameter(index: 0),
      'typeAndOffset' << match.LoadField(slot: 'typeAndOffset'),
      'typeAndOffset_u32' <<
          match.IntConverter('typeAndOffset', from: 'int64', to: 'uint32'),
      'typeIndex' <<
          match.BinaryUint32Op('typeAndOffset_u32', 'int 255', op_kind: '&'),
      match.Branch(
        match.EqualityCompare('typeIndex', 'int 10', kind: '=='),
        ifTrue: 'B9',
        ifFalse: 'B4',
      ),
    ]),
    'B9' << match.block('Target', [match.Goto('B8')]),
    'B4' <<
        match.block('Target', [
          match.Branch(
            match.EqualityCompare('typeIndex', 'int 20', kind: '=='),
            ifTrue: 'B10',
            ifFalse: 'B5',
          ),
        ]),
    'B10' << match.block('Target', [match.Goto('B8')]),
    'B5' <<
        match.block('Target', [
          match.Branch(
            match.EqualityCompare('typeIndex', 'int 30', kind: '=='),
            ifTrue: 'B11',
            ifFalse: 'B6',
          ),
        ]),
    'B11' << match.block('Target', [match.Goto('B8')]),
    'B6' <<
        match.block('Target', [
          match.Branch(
            match.EqualityCompare('typeIndex', 'int 40', kind: '=='),
            ifTrue: 'B12',
            ifFalse: 'B7',
          ),
        ]),
    'B12' << match.block('Target', [match.Goto('B8')]),
    'B8' << match.block('Join', [match.Goto('B3')]),
    'B7' <<
        match.block('Target', [
          'v23' << match.EqualityCompare('typeIndex', 'int 50', kind: '=='),
          match.Goto('B3'),
        ]),
    'B3' <<
        match.block('Join', [
          'v14' << match.Phi('v23', match.any),
          match.DartReturn('v14'),
        ]),
  ]);
}

void main() {
  looksLikeExpressionStart(Token(TokenType.T10, 5));
  looksLikeExpressionStart(Token(TokenType.T60, 10));
}
