; NOTE: Assertions have been autogenerated by utils/update_test_checks.py

; RUN: opt -mtriple=colossus-graphcore-elf -mattr=+ipu1 -S -consthoist < %s | FileCheck %s --check-prefixes CHECK
; RUN: opt -mtriple=colossus-graphcore-elf -mattr=+ipu1,+supervisor -S -consthoist < %s | FileCheck %s --check-prefixes CHECK

; RUN: opt -mtriple=colossus-graphcore-elf -mattr=+ipu2 -S -consthoist < %s | FileCheck %s --check-prefixes CHECK
; RUN: opt -mtriple=colossus-graphcore-elf -mattr=+ipu2,+supervisor -S -consthoist < %s | FileCheck %s --check-prefixes CHECK


; ConstantHoisting pass only targets non-nested integer immediate constants
; which is why only integers are covered in this file.
define i32 @ZimmNoHoist(i32 %a, i32 %b) nounwind {
;
; CHECK-LABEL: @ZimmNoHoist(
; CHECK-NEXT:    [[TMP1:%.*]] = or i32 [[A:%.*]], 4095
; CHECK-NEXT:    [[TMP2:%.*]] = and i32 [[B:%.*]], 4095
; CHECK-NEXT:    [[TMP3:%.*]] = add i32 [[TMP1]], 65535
; CHECK-NEXT:    [[TMP4:%.*]] = sub i32 65535, [[TMP2]]
; CHECK-NEXT:    [[TMP5:%.*]] = xor i32 [[TMP3]], [[TMP4]]
; CHECK-NEXT:    [[TMP6:%.*]] = shl i32 [[A]], 4095
; CHECK-NEXT:    [[TMP7:%.*]] = shl i32 [[A]], 255
; CHECK-NEXT:    [[TMP8:%.*]] = lshr i32 [[A]], 255
; CHECK-NEXT:    [[TMP9:%.*]] = ashr i32 [[A]], 255
; CHECK-NEXT:    ret i32 [[TMP9]]
;
  %1 = or i32 %a, 4095
  %2 = and i32 %b, 4095
  %3 = add i32 %1, 65535
  %4 = sub i32 65535, %2
  %5 = xor i32 %3, %4
  %6 = shl i32 %a, 4095
  %7 = shl i32 %a, 255
  %8 = lshr i32 %a, 255
  %9 = ashr i32 %a, 255
  ret i32 %9
}


define i32 @ZimmHoist(i32 %a, i32 %b) nounwind {
;
; CHECK-LABEL: @ZimmHoist(
; CHECK-NEXT:    [[CONST:%.*]] = bitcast i32 4096 to i32
; CHECK-NEXT:    [[TMP1:%.*]] = or i32 [[A:%.*]], [[CONST]]
; CHECK-NEXT:    [[TMP2:%.*]] = and i32 [[B:%.*]], [[CONST]]
; CHECK-NEXT:    [[CONST_MAT:%.*]] = add i32 [[CONST]], 61440
; CHECK-NEXT:    [[TMP3:%.*]] = add i32 [[TMP1]], [[CONST_MAT]]
; CHECK-NEXT:    [[CONST_MAT1:%.*]] = add i32 [[CONST]], 61440
; CHECK-NEXT:    [[TMP4:%.*]] = sub i32 [[CONST_MAT1]], [[TMP2]]
; CHECK-NEXT:    [[TMP5:%.*]] = xor i32 [[TMP3]], [[TMP4]]
; CHECK-NEXT:    [[TMP6:%.*]] = shl i32 [[A]], [[CONST]]
; CHECK-NEXT:    [[TMP7:%.*]] = lshr i32 [[A]], [[CONST]]
; CHECK-NEXT:    [[TMP8:%.*]] = ashr i32 [[A]], [[CONST]]
; CHECK-NEXT:    [[TMP9:%.*]] = shl i32 [[A]], [[CONST]]
; CHECK-NEXT:    [[TMP10:%.*]] = lshr i32 [[A]], [[CONST]]
; CHECK-NEXT:    [[TMP11:%.*]] = ashr i32 [[A]], [[CONST]]
; CHECK-NEXT:    ret i32 [[TMP11]]
;
  %1 = or i32 %a, 4096
  %2 = and i32 %b, 4096
  %3 = add i32 %1, 65536
  %4 = sub i32 65536, %2
  %5 = xor i32 %3, %4
  %6 = shl i32 %a, 4096
  %7 = lshr i32 %a, 4096
  %8 = ashr i32 %a, 4096
  %9 = shl i32 %a, 4096
  %10 = lshr i32 %a, 4096
  %11 = ashr i32 %a, 4096
  ret i32 %11
}

define i32 @SimmNoHoist(i32 %a, i32 %b) nounwind {
;
; CHECK-LABEL: @SimmNoHoist(
; CHECK-NEXT:    [[TMP1:%.*]] = add i32 [[A:%.*]], -32768
; CHECK-NEXT:    [[TMP2:%.*]] = sub i32 -32768, [[B:%.*]]
; CHECK-NEXT:    [[TMP3:%.*]] = mul i32 [[TMP1]], -32768
; CHECK-NEXT:    [[TMP4:%.*]] = mul i32 [[TMP2]], 32767
; CHECK-NEXT:    [[TMP5:%.*]] = mul i32 [[TMP3]], 32767
; CHECK-NEXT:    [[TMP6:%.*]] = xor i32 [[TMP4]], [[TMP5]]
; CHECK-NEXT:    ret i32 [[TMP6]]
;
  %1 = add i32 %a, -32768
  %2 = sub i32 -32768, %b
  %3 = mul i32 %1, -32768
  %4 = mul i32 %2, 32767
  %5 = mul i32 %3, 32767
  %6 = xor i32 %4, %5
  ret i32 %6
}

define i32 @SimmHoist(i32 %a, i32 %b) nounwind {
;
; CHECK-LABEL: @SimmHoist(
; CHECK-NEXT:    [[CONST1:%.*]] = bitcast i32 -32769 to i32
; CHECK-NEXT:    [[CONST:%.*]] = bitcast i32 32768 to i32
; CHECK-NEXT:    [[TMP1:%.*]] = add i32 [[A:%.*]], [[CONST1]]
; CHECK-NEXT:    [[TMP2:%.*]] = sub i32 [[CONST1]], [[B:%.*]]
; CHECK-NEXT:    [[TMP3:%.*]] = mul i32 [[TMP1]], [[CONST1]]
; CHECK-NEXT:    [[TMP4:%.*]] = mul i32 [[TMP2]], [[CONST]]
; CHECK-NEXT:    [[TMP5:%.*]] = mul i32 [[TMP3]], [[CONST]]
; CHECK-NEXT:    [[TMP6:%.*]] = xor i32 [[TMP4]], [[TMP5]]
; CHECK-NEXT:    ret i32 [[TMP6]]
;
  %1 = add i32 %a, -32769
  %2 = sub i32 -32769, %b
  %3 = mul i32 %1, -32769
  %4 = mul i32 %2, 32768
  %5 = mul i32 %3, 32768
  %6 = xor i32 %4, %5
  ret i32 %6
}

define i32 @ImmzNoHoist(i32 %a, i32 %b, i32 %c, i32 %d) nounwind {
;
; CHECK-LABEL: @ImmzNoHoist(
; CHECK-NEXT:    [[TMP1:%.*]] = or i32 -1048576, [[A:%.*]]
; CHECK-NEXT:    [[TMP2:%.*]] = or i32 -1048576, [[B:%.*]]
; CHECK-NEXT:    [[TMP3:%.*]] = xor i32 [[TMP1]], [[TMP2]]
; CHECK-NEXT:    [[TMP4:%.*]] = or i32 -1048576, [[C:%.*]]
; CHECK-NEXT:    [[TMP5:%.*]] = or i32 -1048576, [[D:%.*]]
; CHECK-NEXT:    [[TMP6:%.*]] = xor i32 [[TMP4]], [[TMP5]]
; CHECK-NEXT:    [[TMP7:%.*]] = xor i32 [[TMP3]], [[TMP6]]
; CHECK-NEXT:    ret i32 [[TMP7]]
;
  %1 = or i32 4293918720, %a
  %2 = or i32 4293918720, %b
  %3 = xor i32 %1, %2
  %4 = or i32 -1048576, %c
  %5 = or i32 -1048576, %d
  %6 = xor i32 %4, %5
  %7 = xor i32 %3, %6
  ret i32 %7
}

define i32 @ImmzHoist(i32 %a, i32 %b, i32 %c, i32 %d) nounwind {
;
; CHECK-LABEL: @ImmzHoist(
; CHECK-NEXT:    [[CONST:%.*]] = bitcast i32 -524288 to i32
; CHECK-NEXT:    [[TMP1:%.*]] = or i32 [[CONST]], [[A:%.*]]
; CHECK-NEXT:    [[TMP2:%.*]] = or i32 [[CONST]], [[B:%.*]]
; CHECK-NEXT:    [[TMP3:%.*]] = xor i32 [[TMP1]], [[TMP2]]
; CHECK-NEXT:    [[TMP4:%.*]] = or i32 [[CONST]], [[C:%.*]]
; CHECK-NEXT:    [[TMP5:%.*]] = or i32 [[CONST]], [[D:%.*]]
; CHECK-NEXT:    [[TMP6:%.*]] = xor i32 [[TMP4]], [[TMP5]]
; CHECK-NEXT:    [[TMP7:%.*]] = xor i32 [[TMP3]], [[TMP6]]
; CHECK-NEXT:    ret i32 [[TMP7]]
;
  %1 = or i32 4294443008, %a
  %2 = or i32 4294443008, %b
  %3 = xor i32 %1, %2
  %4 = or i32 -524288, %c
  %5 = or i32 -524288, %d
  %6 = xor i32 %4, %5
  %7 = xor i32 %3, %6
  ret i32 %7
}

define i32 @MaterializationCost(i32 %a, i32 %b, i32 %c, i32 %d) nounwind {
;
; CHECK-LABEL: @MaterializationCost(
; CHECK-NEXT:    [[CONST:%.*]] = bitcast i32 1048576 to i32
; CHECK-NEXT:    [[CONST_MAT:%.*]] = add i32 [[CONST]], -1
; CHECK-NEXT:    [[TMP1:%.*]] = add i32 [[A:%.*]], [[CONST_MAT]]
; CHECK-NEXT:    [[CONST_MAT1:%.*]] = add i32 [[CONST]], -1
; CHECK-NEXT:    [[TMP2:%.*]] = add i32 [[B:%.*]], [[CONST_MAT1]]
; CHECK-NEXT:    [[TMP3:%.*]] = add i32 [[TMP1]], [[TMP2]]
; CHECK-NEXT:    [[TMP4:%.*]] = add i32 [[C:%.*]], [[CONST]]
; CHECK-NEXT:    [[TMP5:%.*]] = add i32 [[D:%.*]], [[CONST]]
; CHECK-NEXT:    [[TMP6:%.*]] = add i32 [[TMP4]], [[TMP5]]
; CHECK-NEXT:    [[TMP7:%.*]] = add i32 [[TMP3]], [[TMP6]]
; CHECK-NEXT:    ret i32 [[TMP7]]
;
  %1 = add i32 %a, 1048575
  %2 = add i32 %b, 1048575
  %3 = add i32 %1, %2
  %4 = add i32 %c, 1048576
  %5 = add i32 %d, 1048576
  %6 = add i32 %4, %5
  %7 = add i32 %3, %6
  ret i32 %7
}

define i64 @UnhoistableType(i64 %a) nounwind {
;
; CHECK-LABEL: @UnhoistableType(
; CHECK-NEXT:    [[TMP1:%.*]] = add i64 [[A:%.*]], 1
; CHECK-NEXT:    [[TMP2:%.*]] = add i64 [[TMP1]], 1
; CHECK-NEXT:    ret i64 [[TMP2]]
;
  %1 = add i64 %a, 1
  %2 = add i64 %1, 1
  ret i64 %2
}

