; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
;=========================== begin_copyright_notice ============================
;
; Copyright (C) 2024 Intel Corporation
;
; SPDX-License-Identifier: MIT
;
;============================ end_copyright_notice =============================
;
; RUN: igc_opt --igc-scalarize -S < %s | FileCheck %s
; ------------------------------------------------
; ScalarizeFunction
; ------------------------------------------------
; The pass should break vector operation into many scalar operations
; ------------------------------------------------

define double @basic(<2 x double*> %pointers) {
; CHECK-LABEL: define double @basic(
; CHECK-SAME: <2 x double*> [[POINTERS:%.*]]) {
; CHECK-NEXT:    [[POINTERS_SCALAR:%.*]] = extractelement <2 x double*> [[POINTERS]], i32 0
; CHECK-NEXT:    [[POINTERS_SCALAR1:%.*]] = extractelement <2 x double*> [[POINTERS]], i32 1
; CHECK-NEXT:    [[POINTER_TO_DOUBLE2:%.*]] = getelementptr double, double* [[POINTERS_SCALAR]], i32 1
; CHECK-NEXT:    [[POINTER_TO_DOUBLE_ASSEMBLED_VECT:%.*]] = insertelement <2 x double*> undef, double* [[POINTER_TO_DOUBLE2]], i32 0
; CHECK-NEXT:    [[POINTER_TO_DOUBLE3:%.*]] = getelementptr double, double* [[POINTERS_SCALAR1]], i32 1
; CHECK-NEXT:    [[POINTER_TO_DOUBLE_ASSEMBLED_VECT4:%.*]] = insertelement <2 x double*> [[POINTER_TO_DOUBLE_ASSEMBLED_VECT]], double* [[POINTER_TO_DOUBLE3]], i32 1
; CHECK-NEXT:    [[VAL0:%.*]] = load double, double* [[POINTER_TO_DOUBLE2]], align 8
; CHECK-NEXT:    [[VAL1:%.*]] = load double, double* [[POINTER_TO_DOUBLE3]], align 8
; CHECK-NEXT:    [[RETURN:%.*]] = fadd double [[VAL0]], [[VAL1]]
; CHECK-NEXT:    ret double [[RETURN]]
;
  %pointer_to_double = getelementptr double, <2 x double*> %pointers, i32 1

  %ptr0 = extractelement <2 x double*> %pointer_to_double, i32 0
  %ptr1 = extractelement <2 x double*> %pointer_to_double, i32 1
  %val0 = load double, double* %ptr0
  %val1 = load double, double* %ptr1
  %return = fadd double %val0, %val1
  ret double %return
}

define double @should_preserve_metadata(<2 x double*> %pointers) {
; CHECK-LABEL: define double @should_preserve_metadata(
; CHECK-SAME: <2 x double*> [[POINTERS:%.*]]) {
; CHECK-NEXT:    [[POINTERS_SCALAR:%.*]] = extractelement <2 x double*> [[POINTERS]], i32 0
; CHECK-NEXT:    [[POINTERS_SCALAR1:%.*]] = extractelement <2 x double*> [[POINTERS]], i32 1
; CHECK-NEXT:    [[POINTER_TO_DOUBLE2:%.*]] = getelementptr double, double* [[POINTERS_SCALAR]], i32 1, !any_metadata [[META0:![0-9]+]]
; CHECK-NEXT:    [[POINTER_TO_DOUBLE_ASSEMBLED_VECT:%.*]] = insertelement <2 x double*> undef, double* [[POINTER_TO_DOUBLE2]], i32 0
; CHECK-NEXT:    [[POINTER_TO_DOUBLE3:%.*]] = getelementptr double, double* [[POINTERS_SCALAR1]], i32 1, !any_metadata [[META0]]
; CHECK-NEXT:    [[POINTER_TO_DOUBLE_ASSEMBLED_VECT4:%.*]] = insertelement <2 x double*> [[POINTER_TO_DOUBLE_ASSEMBLED_VECT]], double* [[POINTER_TO_DOUBLE3]], i32 1
; CHECK-NEXT:    [[VAL0:%.*]] = load double, double* [[POINTER_TO_DOUBLE2]], align 8
; CHECK-NEXT:    [[VAL1:%.*]] = load double, double* [[POINTER_TO_DOUBLE3]], align 8
; CHECK-NEXT:    [[RETURN:%.*]] = fadd double [[VAL0]], [[VAL1]]
; CHECK-NEXT:    ret double [[RETURN]]
;
  %pointer_to_double = getelementptr double, <2 x double*> %pointers, i32 1, !any_metadata !{i32 0}

  %ptr0 = extractelement <2 x double*> %pointer_to_double, i32 0
  %ptr1 = extractelement <2 x double*> %pointer_to_double, i32 1
  %val0 = load double, double* %ptr0
  %val1 = load double, double* %ptr1
  %return = fadd double %val0, %val1
  ret double %return
}

define double @should_work_with_vector_of_indices(<2 x double*> %pointers) {
; CHECK-LABEL: define double @should_work_with_vector_of_indices(
; CHECK-SAME: <2 x double*> [[POINTERS:%.*]]) {
; CHECK-NEXT:    [[POINTERS_SCALAR:%.*]] = extractelement <2 x double*> [[POINTERS]], i32 0
; CHECK-NEXT:    [[POINTERS_SCALAR1:%.*]] = extractelement <2 x double*> [[POINTERS]], i32 1
; CHECK-NEXT:    [[POINTERS_TO_DOUBLE2:%.*]] = getelementptr double, double* [[POINTERS_SCALAR]], i32 0
; CHECK-NEXT:    [[POINTERS_TO_DOUBLE_ASSEMBLED_VECT:%.*]] = insertelement <2 x double*> undef, double* [[POINTERS_TO_DOUBLE2]], i32 0
; CHECK-NEXT:    [[POINTERS_TO_DOUBLE3:%.*]] = getelementptr double, double* [[POINTERS_SCALAR1]], i32 1
; CHECK-NEXT:    [[POINTERS_TO_DOUBLE_ASSEMBLED_VECT4:%.*]] = insertelement <2 x double*> [[POINTERS_TO_DOUBLE_ASSEMBLED_VECT]], double* [[POINTERS_TO_DOUBLE3]], i32 1
; CHECK-NEXT:    [[VAL0:%.*]] = load double, double* [[POINTERS_TO_DOUBLE2]], align 8
; CHECK-NEXT:    [[VAL1:%.*]] = load double, double* [[POINTERS_TO_DOUBLE3]], align 8
; CHECK-NEXT:    [[RETURN:%.*]] = fadd double [[VAL0]], [[VAL1]]
; CHECK-NEXT:    ret double [[RETURN]]
;
  %pointers_to_double = getelementptr double, <2 x double*> %pointers, <2 x i32> <i32 0, i32 1>

  %ptr0 = extractelement <2 x double*> %pointers_to_double, i32 0
  %ptr1 = extractelement <2 x double*> %pointers_to_double, i32 1
  %val0 = load double, double* %ptr0
  %val1 = load double, double* %ptr1
  %return = fadd double %val0, %val1
  ret double %return
}

define i64 @should_work_with_different_value_type(<2 x i64*> %pointers) {
; CHECK-LABEL: define i64 @should_work_with_different_value_type(
; CHECK-SAME: <2 x i64*> [[POINTERS:%.*]]) {
; CHECK-NEXT:    [[POINTERS_SCALAR:%.*]] = extractelement <2 x i64*> [[POINTERS]], i32 0
; CHECK-NEXT:    [[POINTERS_SCALAR1:%.*]] = extractelement <2 x i64*> [[POINTERS]], i32 1
; CHECK-NEXT:    [[POINTER_TO_I642:%.*]] = getelementptr i64, i64* [[POINTERS_SCALAR]], i32 1
; CHECK-NEXT:    [[POINTER_TO_I64_ASSEMBLED_VECT:%.*]] = insertelement <2 x i64*> undef, i64* [[POINTER_TO_I642]], i32 0
; CHECK-NEXT:    [[POINTER_TO_I643:%.*]] = getelementptr i64, i64* [[POINTERS_SCALAR1]], i32 1
; CHECK-NEXT:    [[POINTER_TO_I64_ASSEMBLED_VECT4:%.*]] = insertelement <2 x i64*> [[POINTER_TO_I64_ASSEMBLED_VECT]], i64* [[POINTER_TO_I643]], i32 1
; CHECK-NEXT:    [[VAL0:%.*]] = load i64, i64* [[POINTER_TO_I642]], align 4
; CHECK-NEXT:    [[VAL1:%.*]] = load i64, i64* [[POINTER_TO_I643]], align 4
; CHECK-NEXT:    [[RETURN:%.*]] = add i64 [[VAL0]], [[VAL1]]
; CHECK-NEXT:    ret i64 [[RETURN]]
;
  %pointer_to_i64 = getelementptr i64, <2 x i64*> %pointers, i32 1

  %ptr0 = extractelement <2 x i64*> %pointer_to_i64, i32 0
  %ptr1 = extractelement <2 x i64*> %pointer_to_i64, i32 1
  %val0 = load i64, i64* %ptr0
  %val1 = load i64, i64* %ptr1
  %return = add i64 %val0, %val1
  ret i64 %return
}

define double @should_work_with_larger_vector_size(<16 x double*> %pointers) {
; CHECK-LABEL: define double @should_work_with_larger_vector_size(
; CHECK-SAME: <16 x double*> [[POINTERS:%.*]]) {
; CHECK-NEXT:    [[POINTERS_SCALAR:%.*]] = extractelement <16 x double*> [[POINTERS]], i32 0
; CHECK-NEXT:    [[POINTERS_SCALAR1:%.*]] = extractelement <16 x double*> [[POINTERS]], i32 1
; CHECK-NEXT:    [[POINTERS_SCALAR2:%.*]] = extractelement <16 x double*> [[POINTERS]], i32 2
; CHECK-NEXT:    [[POINTERS_SCALAR3:%.*]] = extractelement <16 x double*> [[POINTERS]], i32 3
; CHECK-NEXT:    [[POINTERS_SCALAR4:%.*]] = extractelement <16 x double*> [[POINTERS]], i32 4
; CHECK-NEXT:    [[POINTERS_SCALAR5:%.*]] = extractelement <16 x double*> [[POINTERS]], i32 5
; CHECK-NEXT:    [[POINTERS_SCALAR6:%.*]] = extractelement <16 x double*> [[POINTERS]], i32 6
; CHECK-NEXT:    [[POINTERS_SCALAR7:%.*]] = extractelement <16 x double*> [[POINTERS]], i32 7
; CHECK-NEXT:    [[POINTERS_SCALAR8:%.*]] = extractelement <16 x double*> [[POINTERS]], i32 8
; CHECK-NEXT:    [[POINTERS_SCALAR9:%.*]] = extractelement <16 x double*> [[POINTERS]], i32 9
; CHECK-NEXT:    [[POINTERS_SCALAR10:%.*]] = extractelement <16 x double*> [[POINTERS]], i32 10
; CHECK-NEXT:    [[POINTERS_SCALAR11:%.*]] = extractelement <16 x double*> [[POINTERS]], i32 11
; CHECK-NEXT:    [[POINTERS_SCALAR12:%.*]] = extractelement <16 x double*> [[POINTERS]], i32 12
; CHECK-NEXT:    [[POINTERS_SCALAR13:%.*]] = extractelement <16 x double*> [[POINTERS]], i32 13
; CHECK-NEXT:    [[POINTERS_SCALAR14:%.*]] = extractelement <16 x double*> [[POINTERS]], i32 14
; CHECK-NEXT:    [[POINTERS_SCALAR15:%.*]] = extractelement <16 x double*> [[POINTERS]], i32 15
; CHECK-NEXT:    [[POINTER_TO_DOUBLE16:%.*]] = getelementptr double, double* [[POINTERS_SCALAR]], i32 1
; CHECK-NEXT:    [[POINTER_TO_DOUBLE_ASSEMBLED_VECT:%.*]] = insertelement <16 x double*> undef, double* [[POINTER_TO_DOUBLE16]], i32 0
; CHECK-NEXT:    [[POINTER_TO_DOUBLE17:%.*]] = getelementptr double, double* [[POINTERS_SCALAR1]], i32 1
; CHECK-NEXT:    [[POINTER_TO_DOUBLE_ASSEMBLED_VECT18:%.*]] = insertelement <16 x double*> [[POINTER_TO_DOUBLE_ASSEMBLED_VECT]], double* [[POINTER_TO_DOUBLE17]], i32 1
; CHECK-NEXT:    [[POINTER_TO_DOUBLE19:%.*]] = getelementptr double, double* [[POINTERS_SCALAR2]], i32 1
; CHECK-NEXT:    [[POINTER_TO_DOUBLE_ASSEMBLED_VECT20:%.*]] = insertelement <16 x double*> [[POINTER_TO_DOUBLE_ASSEMBLED_VECT18]], double* [[POINTER_TO_DOUBLE19]], i32 2
; CHECK-NEXT:    [[POINTER_TO_DOUBLE21:%.*]] = getelementptr double, double* [[POINTERS_SCALAR3]], i32 1
; CHECK-NEXT:    [[POINTER_TO_DOUBLE_ASSEMBLED_VECT22:%.*]] = insertelement <16 x double*> [[POINTER_TO_DOUBLE_ASSEMBLED_VECT20]], double* [[POINTER_TO_DOUBLE21]], i32 3
; CHECK-NEXT:    [[POINTER_TO_DOUBLE23:%.*]] = getelementptr double, double* [[POINTERS_SCALAR4]], i32 1
; CHECK-NEXT:    [[POINTER_TO_DOUBLE_ASSEMBLED_VECT24:%.*]] = insertelement <16 x double*> [[POINTER_TO_DOUBLE_ASSEMBLED_VECT22]], double* [[POINTER_TO_DOUBLE23]], i32 4
; CHECK-NEXT:    [[POINTER_TO_DOUBLE25:%.*]] = getelementptr double, double* [[POINTERS_SCALAR5]], i32 1
; CHECK-NEXT:    [[POINTER_TO_DOUBLE_ASSEMBLED_VECT26:%.*]] = insertelement <16 x double*> [[POINTER_TO_DOUBLE_ASSEMBLED_VECT24]], double* [[POINTER_TO_DOUBLE25]], i32 5
; CHECK-NEXT:    [[POINTER_TO_DOUBLE27:%.*]] = getelementptr double, double* [[POINTERS_SCALAR6]], i32 1
; CHECK-NEXT:    [[POINTER_TO_DOUBLE_ASSEMBLED_VECT28:%.*]] = insertelement <16 x double*> [[POINTER_TO_DOUBLE_ASSEMBLED_VECT26]], double* [[POINTER_TO_DOUBLE27]], i32 6
; CHECK-NEXT:    [[POINTER_TO_DOUBLE29:%.*]] = getelementptr double, double* [[POINTERS_SCALAR7]], i32 1
; CHECK-NEXT:    [[POINTER_TO_DOUBLE_ASSEMBLED_VECT30:%.*]] = insertelement <16 x double*> [[POINTER_TO_DOUBLE_ASSEMBLED_VECT28]], double* [[POINTER_TO_DOUBLE29]], i32 7
; CHECK-NEXT:    [[POINTER_TO_DOUBLE31:%.*]] = getelementptr double, double* [[POINTERS_SCALAR8]], i32 1
; CHECK-NEXT:    [[POINTER_TO_DOUBLE_ASSEMBLED_VECT32:%.*]] = insertelement <16 x double*> [[POINTER_TO_DOUBLE_ASSEMBLED_VECT30]], double* [[POINTER_TO_DOUBLE31]], i32 8
; CHECK-NEXT:    [[POINTER_TO_DOUBLE33:%.*]] = getelementptr double, double* [[POINTERS_SCALAR9]], i32 1
; CHECK-NEXT:    [[POINTER_TO_DOUBLE_ASSEMBLED_VECT34:%.*]] = insertelement <16 x double*> [[POINTER_TO_DOUBLE_ASSEMBLED_VECT32]], double* [[POINTER_TO_DOUBLE33]], i32 9
; CHECK-NEXT:    [[POINTER_TO_DOUBLE35:%.*]] = getelementptr double, double* [[POINTERS_SCALAR10]], i32 1
; CHECK-NEXT:    [[POINTER_TO_DOUBLE_ASSEMBLED_VECT36:%.*]] = insertelement <16 x double*> [[POINTER_TO_DOUBLE_ASSEMBLED_VECT34]], double* [[POINTER_TO_DOUBLE35]], i32 10
; CHECK-NEXT:    [[POINTER_TO_DOUBLE37:%.*]] = getelementptr double, double* [[POINTERS_SCALAR11]], i32 1
; CHECK-NEXT:    [[POINTER_TO_DOUBLE_ASSEMBLED_VECT38:%.*]] = insertelement <16 x double*> [[POINTER_TO_DOUBLE_ASSEMBLED_VECT36]], double* [[POINTER_TO_DOUBLE37]], i32 11
; CHECK-NEXT:    [[POINTER_TO_DOUBLE39:%.*]] = getelementptr double, double* [[POINTERS_SCALAR12]], i32 1
; CHECK-NEXT:    [[POINTER_TO_DOUBLE_ASSEMBLED_VECT40:%.*]] = insertelement <16 x double*> [[POINTER_TO_DOUBLE_ASSEMBLED_VECT38]], double* [[POINTER_TO_DOUBLE39]], i32 12
; CHECK-NEXT:    [[POINTER_TO_DOUBLE41:%.*]] = getelementptr double, double* [[POINTERS_SCALAR13]], i32 1
; CHECK-NEXT:    [[POINTER_TO_DOUBLE_ASSEMBLED_VECT42:%.*]] = insertelement <16 x double*> [[POINTER_TO_DOUBLE_ASSEMBLED_VECT40]], double* [[POINTER_TO_DOUBLE41]], i32 13
; CHECK-NEXT:    [[POINTER_TO_DOUBLE43:%.*]] = getelementptr double, double* [[POINTERS_SCALAR14]], i32 1
; CHECK-NEXT:    [[POINTER_TO_DOUBLE_ASSEMBLED_VECT44:%.*]] = insertelement <16 x double*> [[POINTER_TO_DOUBLE_ASSEMBLED_VECT42]], double* [[POINTER_TO_DOUBLE43]], i32 14
; CHECK-NEXT:    [[POINTER_TO_DOUBLE45:%.*]] = getelementptr double, double* [[POINTERS_SCALAR15]], i32 1
; CHECK-NEXT:    [[POINTER_TO_DOUBLE_ASSEMBLED_VECT46:%.*]] = insertelement <16 x double*> [[POINTER_TO_DOUBLE_ASSEMBLED_VECT44]], double* [[POINTER_TO_DOUBLE45]], i32 15
; CHECK-NEXT:    [[VAL0:%.*]] = load double, double* [[POINTER_TO_DOUBLE16]], align 8
; CHECK-NEXT:    [[VAL1:%.*]] = load double, double* [[POINTER_TO_DOUBLE17]], align 8
; CHECK-NEXT:    [[RETURN:%.*]] = fadd double [[VAL0]], [[VAL1]]
; CHECK-NEXT:    ret double [[RETURN]]
;
  %pointer_to_double = getelementptr double, <16 x double*> %pointers, i32 1

  %ptr0 = extractelement <16 x double*> %pointer_to_double, i32 0
  %ptr1 = extractelement <16 x double*> %pointer_to_double, i32 1
  %val0 = load double, double* %ptr0
  %val1 = load double, double* %ptr1
  %return = fadd double %val0, %val1
  ret double %return
}

%some_type = type {i64, i32}

; checks early out for non: <result> = getelementptr <ty>, <N x ptr> <ptrval>, <vector index type> <idx>
define i64 @should_not_scalarize_with_more_then_one_index(%some_type* %pointer) {
; CHECK-LABEL: @should_not_scalarize_with_more_then_one_index(
; CHECK-NEXT:    [[POINTER_TO_INT:%.*]] = getelementptr [[SOME_TYPE:%.*]], %some_type* [[POINTER:%.*]], i32 0, i32 0
; CHECK-NEXT:    [[VAL0:%.*]] = load i64, i64* [[POINTER_TO_INT]], align 4
; CHECK-NEXT:    [[RETURN:%.*]] = add i64 [[VAL0]], [[VAL0]]
; CHECK-NEXT:    ret i64 [[RETURN]]
;
  %pointer_to_int = getelementptr %some_type, %some_type* %pointer, i32 0, i32 0

  %val0 = load i64, i64* %pointer_to_int
  %return = add i64 %val0, %val0
  ret i64 %return
}

; checks early out for non: <result> = getelementptr <ty>, <N x ptr> <ptrval>, <vector index type> <idx>
define i64 @should_scalarize_only_vectors(%some_type* %pointer) {
; CHECK-LABEL: @should_scalarize_only_vectors(
; CHECK-NEXT:    [[POINTER_SOME_TYPE:%.*]] = getelementptr [[SOME_TYPE:%.*]], %some_type* [[POINTER:%.*]], i32 1
; CHECK-NEXT:    [[VAL:%.*]] = load [[SOME_TYPE]], %some_type* [[POINTER_SOME_TYPE]], align 4
; CHECK-NEXT:    [[VAL0:%.*]] = extractvalue [[SOME_TYPE]] [[VAL]], 0
; CHECK-NEXT:    [[VAL1:%.*]] = extractvalue [[SOME_TYPE]] [[VAL]], 0
; CHECK-NEXT:    [[RETURN:%.*]] = add i64 [[VAL0]], [[VAL1]]
; CHECK-NEXT:    ret i64 [[RETURN]]
;
  %pointer_some_type = getelementptr %some_type, %some_type* %pointer, i32 1

  %val = load %some_type, %some_type* %pointer_some_type

  %val0 = extractvalue %some_type %val, 0
  %val1 = extractvalue %some_type %val, 0
  %return = add i64 %val0, %val1
  ret i64 %return
}

; CHECK: [[META0]] = !{i32 0}