; 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 %s --opaque-pointers -S -o - -types-legalization-pass | FileCheck %s
; REQUIRES: llvm-14-plus

; Should resolve value through insertelement instruction

define spir_kernel void @cover.ResolveValue.cast.to.InsertElementInst()
; CHECK-LABEL: define spir_kernel void @cover.ResolveValue.cast.to.InsertElementInst() {
; CHECK-NEXT:    [[VECTOR_PTR:%.*]] = alloca <2 x i16>, align 1
; CHECK-NEXT:    [[VECTOR:%.*]] = load <2 x i16>, ptr [[VECTOR_PTR]], align 4
; CHECK-NEXT:    [[TMP1:%.*]] = insertelement <2 x i16> [[VECTOR]], i16 999, i32 0
; CHECK-NEXT:    store <2 x i16> [[TMP1]], ptr [[VECTOR_PTR]], align 1
; CHECK-NEXT:    ret void
;
{
  %vector.ptr = alloca <2 x i16>, align 1
  %vector = load <2 x i16>, ptr %vector.ptr
  %1 = insertelement <2 x i16> %vector, i16 999, i32 0
  store <2 x i16> %1, ptr %vector.ptr, align 1
  ret void
}

; Should resolve value from intrinsic instruction

declare {i32, i1} @llvm.uadd.with.overflow.i32(i32 %a, i32 %b)

define i32 @cover.ResolveValue.cast.to.IntrinsicInst(i32 %a, i32 %b)
; CHECK-LABEL: define i32 @cover.ResolveValue.cast.to.IntrinsicInst(
; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
; CHECK-NEXT:  [[ENTRY:.*:]]
; CHECK-NEXT:    [[RES:%.*]] = call { i32, i1 } @llvm.uadd.with.overflow.i32(i32 [[A]], i32 [[B]])
; CHECK-NEXT:    [[TMP0:%.*]] = extractvalue { i32, i1 } [[RES]], 1
; CHECK-NEXT:    br i1 [[TMP0]], label %[[OVERFLOW:.*]], label %[[NORMAL:.*]]
; CHECK:       [[OVERFLOW]]:
; CHECK-NEXT:    br label %[[EXIT:.*]]
; CHECK:       [[NORMAL]]:
; CHECK-NEXT:    [[TMP1:%.*]] = extractvalue { i32, i1 } [[RES]], 0
; CHECK-NEXT:    br label %[[EXIT]]
; CHECK:       [[EXIT]]:
; CHECK-NEXT:    [[END_VALUE:%.*]] = phi i32 [ [[A]], %[[OVERFLOW]] ], [ [[TMP1]], %[[NORMAL]] ]
; CHECK-NEXT:    ret i32 [[END_VALUE]]
;
{
entry:
  %res = call {i32, i1} @llvm.uadd.with.overflow.i32(i32 %a, i32 %b)
  %obit = extractvalue {i32, i1} %res, 1
  br i1 %obit, label %overflow, label %normal
overflow:
  br label %exit
normal:
  %sum = extractvalue {i32, i1} %res, 0
  br label %exit
exit:
  %end.value = phi i32 [%a, %overflow], [%sum, %normal]
  ret i32 %end.value
}

; Should resolve value from select instruction

define spir_kernel void @cover.ResolveValue.cast.to.SelectInst(i1 %condition)
; CHECK-LABEL: define spir_kernel void @cover.ResolveValue.cast.to.SelectInst(
; CHECK-SAME: i1 [[CONDITION:%.*]]) {
; CHECK-NEXT:    [[VECTOR_PTR:%.*]] = alloca <2 x i16>, align 1
; CHECK-NEXT:    [[VECTOR:%.*]] = load <2 x i16>, ptr [[VECTOR_PTR]], align 4
; CHECK-NEXT:    [[FINAL_VECTOR:%.*]] = insertelement <2 x i16> [[VECTOR]], i16 999, i32 0
; CHECK-NEXT:    store <2 x i16> [[FINAL_VECTOR]], ptr [[VECTOR_PTR]], align 1
; CHECK-NEXT:    [[ARRAY_PTR:%.*]] = alloca [2 x <2 x i16>], align 1
; CHECK-NEXT:    [[TMP1:%.*]] = getelementptr [2 x <2 x i16>], ptr [[ARRAY_PTR]], i32 0, i32 1
; CHECK-NEXT:    [[TMP2:%.*]] = load <2 x i16>, ptr [[TMP1]], align 4
; CHECK-NEXT:    [[ARRAY:%.*]] = load [2 x <2 x i16>], ptr [[ARRAY_PTR]], align 4
; CHECK-NEXT:    [[ARRAY1:%.*]] = insertvalue [2 x <2 x i16>] [[ARRAY]], <2 x i16> [[FINAL_VECTOR]], 0
; CHECK-NEXT:    [[ARRAY2:%.*]] = insertvalue [2 x <2 x i16>] [[ARRAY1]], <2 x i16> [[FINAL_VECTOR]], 1
; CHECK-NEXT:    [[TMP3:%.*]] = select i1 [[CONDITION]], <2 x i16> [[FINAL_VECTOR]], <2 x i16> [[FINAL_VECTOR]]
; CHECK-NEXT:    [[TMP4:%.*]] = select i1 [[CONDITION]], <2 x i16> [[TMP2]], <2 x i16> [[FINAL_VECTOR]]
; CHECK-NEXT:    [[FINAL_ARRAY:%.*]] = select i1 [[CONDITION]], [2 x <2 x i16>] [[ARRAY1]], [2 x <2 x i16>] [[ARRAY2]]
; CHECK-NEXT:    [[TMP5:%.*]] = getelementptr [2 x <2 x i16>], ptr [[ARRAY_PTR]], i32 0, i32 0
; CHECK-NEXT:    store <2 x i16> [[TMP3]], ptr [[TMP5]], align 4
; CHECK-NEXT:    [[TMP6:%.*]] = getelementptr [2 x <2 x i16>], ptr [[ARRAY_PTR]], i32 0, i32 1
; CHECK-NEXT:    store <2 x i16> [[TMP4]], ptr [[TMP6]], align 4
; CHECK-NEXT:    ret void
;
{
  %vector.ptr = alloca <2 x i16>, align 1
  %vector = load <2 x i16>, ptr %vector.ptr
  %final.vector = insertelement <2 x i16> %vector, i16 999, i32 0
  store <2 x i16> %final.vector, ptr %vector.ptr, align 1

  %array.ptr = alloca [2 x <2 x i16>], align 1
  %array = load [2 x <2 x i16>], ptr %array.ptr
  %array1 = insertvalue [2 x <2 x i16>] %array, <2 x i16> %final.vector, 0
  %array2 = insertvalue [2 x <2 x i16>] %array1, <2 x i16> %final.vector, 1
  %final.array = select i1 %condition, [2 x <2 x i16>] %array1, [2 x <2 x i16>] %array2
  store [2 x <2 x i16>] %final.array, ptr %array.ptr, align 1
  ret void
}

; Should resolve value from extractvalue instruction

%simple.struct.type = type { i64, float }
%nested.struct.type = type { i32, %simple.struct.type }

define float @cover.ResolveValue.cast.to.SelectInst.ExtractValue(ptr byval(%nested.struct.type) %arg)
; CHECK-LABEL: define float @cover.ResolveValue.cast.to.SelectInst.ExtractValue(
; CHECK-SAME: ptr byval([[NESTED_STRUCT_TYPE:%.*]]) [[ARG:%.*]]) {
; CHECK-NEXT:    [[PTR:%.*]] = getelementptr [[NESTED_STRUCT_TYPE]], ptr [[ARG]], i32 0
; CHECK-NEXT:    [[TMP1:%.*]] = getelementptr [[NESTED_STRUCT_TYPE]], ptr [[PTR]], i32 0, i32 1
; CHECK-NEXT:    [[TMP2:%.*]] = getelementptr [[SIMPLE_STRUCT_TYPE:%.*]], ptr [[TMP1]], i32 0, i32 1
; CHECK-NEXT:    [[TMP3:%.*]] = load float, ptr [[TMP2]], align 4
; CHECK-NEXT:    [[TMP4:%.*]] = load [[SIMPLE_STRUCT_TYPE]], ptr [[TMP1]], align 4
; CHECK-NEXT:    [[NESTED_STRUCT_INSTANCE:%.*]] = load [[NESTED_STRUCT_TYPE]], ptr [[PTR]], align 4
; CHECK-NEXT:    [[RESULT:%.*]] = fmul float [[TMP3]], 4.000000e+00
; CHECK-NEXT:    ret float [[RESULT]]
;
{
  %ptr = getelementptr %nested.struct.type, ptr %arg, i32 0
  %nested.struct.instance = load %nested.struct.type, ptr %ptr

  %simple.struct.instance = extractvalue %nested.struct.type %nested.struct.instance, 1
  %float = extractvalue %simple.struct.type %simple.struct.instance, 1
  %result = fmul float %float, 4.0
  ret float %result
}

define spir_kernel void @cover.ResolveValue.cast.to.Argument.Array.ExtractValue([2 x float] %arg.array)
; CHECK-LABEL: define spir_kernel void @cover.ResolveValue.cast.to.Argument.Array.ExtractValue(
; CHECK-SAME: [2 x float] [[ARG_ARRAY:%.*]]) {
; CHECK-NEXT:    [[TMP1:%.*]] = extractvalue [2 x float] [[ARG_ARRAY]], 1
; CHECK-NEXT:    [[RESULT:%.*]] = fmul float [[TMP1]], 4.000000e+00
; CHECK-NEXT:    ret void
;
{
  %float = extractvalue [2 x float] %arg.array, 1
  %result = fmul float %float, 4.0
  ret void
}

declare {i32, i1} @llvm.sadd.with.overflow.i32(i32 %a, i32 %b)

define i32 @cover.ResolveValue.cast.to.IntrinsicInst.llvm.sadd(i32 %a, i32 %b)
; CHECK-LABEL: define i32 @cover.ResolveValue.cast.to.IntrinsicInst.llvm.sadd(
; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
; CHECK-NEXT:  [[ENTRY:.*:]]
; CHECK-NEXT:    [[RES:%.*]] = call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 [[A]], i32 [[B]])
; CHECK-NEXT:    [[TMP0:%.*]] = extractvalue { i32, i1 } [[RES]], 1
; CHECK-NEXT:    br i1 [[TMP0]], label %[[OVERFLOW:.*]], label %[[NORMAL:.*]]
; CHECK:       [[OVERFLOW]]:
; CHECK-NEXT:    br label %[[EXIT:.*]]
; CHECK:       [[NORMAL]]:
; CHECK-NEXT:    [[TMP1:%.*]] = extractvalue { i32, i1 } [[RES]], 0
; CHECK-NEXT:    br label %[[EXIT]]
; CHECK:       [[EXIT]]:
; CHECK-NEXT:    [[END_VALUE:%.*]] = phi i32 [ [[A]], %[[OVERFLOW]] ], [ [[TMP1]], %[[NORMAL]] ]
; CHECK-NEXT:    ret i32 [[END_VALUE]]
;
{
entry:
  %res = call {i32, i1} @llvm.sadd.with.overflow.i32(i32 %a, i32 %b)
  %obit = extractvalue {i32, i1} %res, 1
  br i1 %obit, label %overflow, label %normal
overflow:
  br label %exit
normal:
  %sum = extractvalue {i32, i1} %res, 0
  br label %exit
exit:
  %end.value = phi i32 [%a, %overflow], [%sum, %normal]
  ret i32 %end.value
}

declare {i32, i1} @llvm.ssub.with.overflow.i32(i32 %a, i32 %b)

define i32 @cover.ResolveValue.cast.to.IntrinsicInst.llvm.ssub(i32 %a, i32 %b)
; CHECK-LABEL: define i32 @cover.ResolveValue.cast.to.IntrinsicInst.llvm.ssub(
; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
; CHECK-NEXT:  [[ENTRY:.*:]]
; CHECK-NEXT:    [[RES:%.*]] = call { i32, i1 } @llvm.ssub.with.overflow.i32(i32 [[A]], i32 [[B]])
; CHECK-NEXT:    [[TMP0:%.*]] = extractvalue { i32, i1 } [[RES]], 1
; CHECK-NEXT:    br i1 [[TMP0]], label %[[OVERFLOW:.*]], label %[[NORMAL:.*]]
; CHECK:       [[OVERFLOW]]:
; CHECK-NEXT:    br label %[[EXIT:.*]]
; CHECK:       [[NORMAL]]:
; CHECK-NEXT:    [[TMP1:%.*]] = extractvalue { i32, i1 } [[RES]], 0
; CHECK-NEXT:    br label %[[EXIT]]
; CHECK:       [[EXIT]]:
; CHECK-NEXT:    [[END_VALUE:%.*]] = phi i32 [ [[A]], %[[OVERFLOW]] ], [ [[TMP1]], %[[NORMAL]] ]
; CHECK-NEXT:    ret i32 [[END_VALUE]]
;
{
entry:
  %res = call {i32, i1} @llvm.ssub.with.overflow.i32(i32 %a, i32 %b)
  %obit = extractvalue {i32, i1} %res, 1
  br i1 %obit, label %overflow, label %normal
overflow:
  br label %exit
normal:
  %sum = extractvalue {i32, i1} %res, 0
  br label %exit
exit:
  %end.value = phi i32 [%a, %overflow], [%sum, %normal]
  ret i32 %end.value
}

declare {i32, i1} @llvm.usub.with.overflow.i32(i32 %a, i32 %b)

define i32 @cover.ResolveValue.cast.to.IntrinsicInst.llvm.usub(i32 %a, i32 %b)
; CHECK-LABEL: define i32 @cover.ResolveValue.cast.to.IntrinsicInst.llvm.usub(
; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
; CHECK-NEXT:  [[ENTRY:.*:]]
; CHECK-NEXT:    [[RES:%.*]] = call { i32, i1 } @llvm.usub.with.overflow.i32(i32 [[A]], i32 [[B]])
; CHECK-NEXT:    [[TMP0:%.*]] = extractvalue { i32, i1 } [[RES]], 1
; CHECK-NEXT:    br i1 [[TMP0]], label %[[OVERFLOW:.*]], label %[[NORMAL:.*]]
; CHECK:       [[OVERFLOW]]:
; CHECK-NEXT:    br label %[[EXIT:.*]]
; CHECK:       [[NORMAL]]:
; CHECK-NEXT:    [[TMP1:%.*]] = extractvalue { i32, i1 } [[RES]], 0
; CHECK-NEXT:    br label %[[EXIT]]
; CHECK:       [[EXIT]]:
; CHECK-NEXT:    [[END_VALUE:%.*]] = phi i32 [ [[A]], %[[OVERFLOW]] ], [ [[TMP1]], %[[NORMAL]] ]
; CHECK-NEXT:    ret i32 [[END_VALUE]]
;
{
entry:
  %res = call {i32, i1} @llvm.usub.with.overflow.i32(i32 %a, i32 %b)
  %obit = extractvalue {i32, i1} %res, 1
  br i1 %obit, label %overflow, label %normal
overflow:
  br label %exit
normal:
  %sum = extractvalue {i32, i1} %res, 0
  br label %exit
exit:
  %end.value = phi i32 [%a, %overflow], [%sum, %normal]
  ret i32 %end.value
}


declare {i32, i1} @llvm.smul.with.overflow.i32(i32 %a, i32 %b)

define i32 @cover.ResolveValue.cast.to.IntrinsicInst.llvm.smul(i32 %a, i32 %b)
; CHECK-LABEL: define i32 @cover.ResolveValue.cast.to.IntrinsicInst.llvm.smul(
; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
; CHECK-NEXT:  [[ENTRY:.*:]]
; CHECK-NEXT:    [[RES:%.*]] = call { i32, i1 } @llvm.smul.with.overflow.i32(i32 [[A]], i32 [[B]])
; CHECK-NEXT:    [[TMP0:%.*]] = extractvalue { i32, i1 } [[RES]], 1
; CHECK-NEXT:    br i1 [[TMP0]], label %[[OVERFLOW:.*]], label %[[NORMAL:.*]]
; CHECK:       [[OVERFLOW]]:
; CHECK-NEXT:    br label %[[EXIT:.*]]
; CHECK:       [[NORMAL]]:
; CHECK-NEXT:    [[TMP1:%.*]] = extractvalue { i32, i1 } [[RES]], 0
; CHECK-NEXT:    br label %[[EXIT]]
; CHECK:       [[EXIT]]:
; CHECK-NEXT:    [[END_VALUE:%.*]] = phi i32 [ [[A]], %[[OVERFLOW]] ], [ [[TMP1]], %[[NORMAL]] ]
; CHECK-NEXT:    ret i32 [[END_VALUE]]
;
{
entry:
  %res = call {i32, i1} @llvm.smul.with.overflow.i32(i32 %a, i32 %b)
  %obit = extractvalue {i32, i1} %res, 1
  br i1 %obit, label %overflow, label %normal
overflow:
  br label %exit
normal:
  %sum = extractvalue {i32, i1} %res, 0
  br label %exit
exit:
  %end.value = phi i32 [%a, %overflow], [%sum, %normal]
  ret i32 %end.value
}

declare {i32, i1} @llvm.umul.with.overflow.i32(i32 %a, i32 %b)

define i32 @cover.ResolveValue.cast.to.IntrinsicInst.llvm.umul(i32 %a, i32 %b)
; CHECK-LABEL: define i32 @cover.ResolveValue.cast.to.IntrinsicInst.llvm.umul(
; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
; CHECK-NEXT:  [[ENTRY:.*:]]
; CHECK-NEXT:    [[RES:%.*]] = call { i32, i1 } @llvm.umul.with.overflow.i32(i32 [[A]], i32 [[B]])
; CHECK-NEXT:    [[TMP0:%.*]] = extractvalue { i32, i1 } [[RES]], 1
; CHECK-NEXT:    br i1 [[TMP0]], label %[[OVERFLOW:.*]], label %[[NORMAL:.*]]
; CHECK:       [[OVERFLOW]]:
; CHECK-NEXT:    br label %[[EXIT:.*]]
; CHECK:       [[NORMAL]]:
; CHECK-NEXT:    [[TMP1:%.*]] = extractvalue { i32, i1 } [[RES]], 0
; CHECK-NEXT:    br label %[[EXIT]]
; CHECK:       [[EXIT]]:
; CHECK-NEXT:    [[END_VALUE:%.*]] = phi i32 [ [[A]], %[[OVERFLOW]] ], [ [[TMP1]], %[[NORMAL]] ]
; CHECK-NEXT:    ret i32 [[END_VALUE]]
;
{
entry:
  %res = call {i32, i1} @llvm.umul.with.overflow.i32(i32 %a, i32 %b)
  %obit = extractvalue {i32, i1} %res, 1
  br i1 %obit, label %overflow, label %normal
overflow:
  br label %exit
normal:
  %sum = extractvalue {i32, i1} %res, 0
  br label %exit
exit:
  %end.value = phi i32 [%a, %overflow], [%sum, %normal]
  ret i32 %end.value
}