// This test checks for the use of the no signed wrap (`nsw`) attribute for arithmetic
// expressions which may suffer from overflow.
// Needed for induction variable simplification dicussed in
// [GitHub issue ISPC/#2460](https://github.com/ispc/ispc/issues/2460).

// RUN: %{ispc} %s --nostdlib --emit-llvm-text --target=host -o - |  FileCheck %s --check-prefix=CHECK_NSW

// REQUIRES: !XE_ENABLED

// CHECK_NSW-LABEL:         define <{{[0-9]*}} x i64> @mul_64
// CHECK_NSW-COUNT-1:         %{{.*}}= mul{{.*}}nsw{{.*}}i64
// CHECK_NSW-LABEL:         define <{{[0-9]*}} x i32> @mul_32
// CHECK_NSW-COUNT-1:         %{{.*}}= mul{{.*}}nsw{{.*}}i32
// CHECK_NSW-LABEL:         define <{{[0-9]*}} x i16> @mul_16
// CHECK_NSW-COUNT-1:         %{{.*}}= mul{{.*}}nsw{{.*}}i16
// CHECK_NSW-LABEL:         define <{{[0-9]*}} x i8> @mul_8
// CHECK_NSW-COUNT-1:         %{{.*}}= mul{{.*}}nsw{{.*}}i8

unmasked int64 mul_64(int64 a, int64 b) { return a * b; }

unmasked int32 mul_32(int32 a, int32 b) { return a * b; }

unmasked int16 mul_16(int16 a, int16 b) { return a * b; }

unmasked int8 mul_8(int8 a, int8 b) { return a * b; }


// CHECK_NSW-LABEL:         define <{{[0-9]*}} x i64> @mul_u64
// CHECK_NSW-NOT:             %{{.*}}= mul{{.*}}nsw{{.*}}i64
// CHECK_NSW-LABEL:         define <{{[0-9]*}} x i32> @mul_u32
// CHECK_NSW-NOT:             %{{.*}}= mul{{.*}}nsw{{.*}}i32
// CHECK_NSW-LABEL:         define <{{[0-9]*}} x i16> @mul_u16
// CHECK_NSW-NOT:             %{{.*}}= mul{{.*}}nsw{{.*}}i16
// CHECK_NSW-LABEL:         define <{{[0-9]*}} x i8> @mul_u8
// CHECK_NSW-NOT:             %{{.*}}= mul{{.*}}nsw{{.*}}i8

unmasked uint64 mul_u64(uint64 a, uint64 b) { return a * b; }

unmasked uint32 mul_u32(uint32 a, uint32 b) { return a * b; }

unmasked uint16 mul_u16(uint16 a, uint16 b) { return a * b; }

unmasked uint8 mul_u8(uint8 a, uint8 b) { return a * b; }


// CHECK_NSW-LABEL:         define <{{[0-9]*}} x i64> @add_64
// CHECK_NSW-COUNT-1:         %{{.*}}= add{{.*}}nsw{{.*}}i64
// CHECK_NSW-LABEL:         define <{{[0-9]*}} x i32> @add_32
// CHECK_NSW-COUNT-1:         %{{.*}}= add{{.*}}nsw{{.*}}i32
// CHECK_NSW-LABEL:         define <{{[0-9]*}} x i16> @add_16
// CHECK_NSW-COUNT-1:         %{{.*}}= add{{.*}}nsw{{.*}}i16
// CHECK_NSW-LABEL:         define <{{[0-9]*}} x i8> @add_8
// CHECK_NSW-COUNT-1:         %{{.*}}= add{{.*}}nsw{{.*}}i8

unmasked int64 add_64(int64 a, int64 b) { return a + b; }

unmasked int32 add_32(int32 a, int32 b) { return a + b; }

unmasked int16 add_16(int16 a, int16 b) { return a + b; }

unmasked int8 add_8(int8 a, int8 b) { return a + b; }


// CHECK_NSW-LABEL:         define <{{[0-9]*}} x i64> @add_u64
// CHECK_NSW-NOT:             %{{.*}}= add{{.*}}nsw{{.*}}i64
// CHECK_NSW-LABEL:         define <{{[0-9]*}} x i32> @add_u32
// CHECK_NSW-NOT:             %{{.*}}= add{{.*}}nsw{{.*}}i32
// CHECK_NSW-LABEL:         define <{{[0-9]*}} x i16> @add_u16
// CHECK_NSW-NOT:             %{{.*}}= add{{.*}}nsw{{.*}}i16
// CHECK_NSW-LABEL:         define <{{[0-9]*}} x i8> @add_u8
// CHECK_NSW-NOT:             %{{.*}}= add{{.*}}nsw{{.*}}i8

unmasked uint64 add_u64(uint64 a, uint64 b) { return a + b; }

unmasked uint32 add_u32(uint32 a, uint32 b) { return a + b; }

unmasked uint16 add_u16(uint16 a, uint16 b) { return a + b; }

unmasked uint8 add_u8(uint8 a, uint8 b) { return a + b; }


// CHECK_NSW-LABEL:         define <{{[0-9]*}} x i64> @sub_64
// CHECK_NSW-COUNT-1:         %{{.*}}= sub{{.*}}nsw{{.*}}i64
// CHECK_NSW-LABEL:         define <{{[0-9]*}} x i32> @sub_32
// CHECK_NSW-COUNT-1:         %{{.*}}= sub{{.*}}nsw{{.*}}i32
// CHECK_NSW-LABEL:         define <{{[0-9]*}} x i16> @sub_16
// CHECK_NSW-COUNT-1:         %{{.*}}= sub{{.*}}nsw{{.*}}i16
// CHECK_NSW-LABEL:         define <{{[0-9]*}} x i8> @sub_8
// CHECK_NSW-COUNT-1:         %{{.*}}= sub{{.*}}nsw{{.*}}i8

unmasked int64 sub_64(int64 a, int64 b) { return a - b; }

unmasked int32 sub_32(int32 a, int32 b) { return a - b; }

unmasked int16 sub_16(int16 a, int16 b) { return a - b; }

unmasked int8 sub_8(int8 a, int8 b) { return a - b; }


// CHECK_NSW-LABEL:         define <{{[0-9]*}} x i64> @sub_u64
// CHECK_NSW-NOT:             %{{.*}}= sub{{.*}}nsw{{.*}}i64
// CHECK_NSW-LABEL:         define <{{[0-9]*}} x i32> @sub_u32
// CHECK_NSW-NOT:             %{{.*}}= sub{{.*}}nsw{{.*}}i32
// CHECK_NSW-LABEL:         define <{{[0-9]*}} x i16> @sub_u16
// CHECK_NSW-NOT:             %{{.*}}= sub{{.*}}nsw{{.*}}i16
// CHECK_NSW-LABEL:         define <{{[0-9]*}} x i8> @sub_u8
// CHECK_NSW-NOT:             %{{.*}}= sub{{.*}}nsw{{.*}}i8

unmasked uint64 sub_u64(uint64 a, uint64 b) { return a - b; }

unmasked uint32 sub_u32(uint32 a, uint32 b) { return a - b; }

unmasked uint16 sub_u16(uint16 a, uint16 b) { return a - b; }

unmasked uint8 sub_u8(uint8 a, uint8 b) { return a - b; }


// CHECK_NSW-LABEL:         define <{{[0-9]*}} x i64> @shl_64
// CHECK_NSW-NOT:             %{{.*}}= shl{{.*}}nsw{{.*}}i64
// CHECK_NSW-LABEL:         define <{{[0-9]*}} x i32> @shl_32
// CHECK_NSW-NOT:             %{{.*}}= shl{{.*}}nsw{{.*}}i32
// CHECK_NSW-LABEL:         define <{{[0-9]*}} x i16> @shl_16
// CHECK_NSW-NOT:             %{{.*}}= shl{{.*}}nsw{{.*}}i16
// CHECK_NSW-LABEL:         define <{{[0-9]*}} x i8> @shl_8
// CHECK_NSW-NOT:             %{{.*}}= shl{{.*}}nsw{{.*}}i8

unmasked int64 shl_64(int64 a, int64 b) { return a << b; }

unmasked int32 shl_32(int32 a, int32 b) { return a << b; }

unmasked int16 shl_16(int16 a, int16 b) { return a << b; }

unmasked int8 shl_8(int8 a, int8 b) { return a << b; }


// CHECK_NSW-LABEL:         define <{{[0-9]*}} x i64> @shl_u64
// CHECK_NSW-NOT:             %{{.*}}= shl{{.*}}nsw{{.*}}i64
// CHECK_NSW-LABEL:         define <{{[0-9]*}} x i32> @shl_u32
// CHECK_NSW-NOT:             %{{.*}}= shl{{.*}}nsw{{.*}}i32
// CHECK_NSW-LABEL:         define <{{[0-9]*}} x i16> @shl_u16
// CHECK_NSW-NOT:             %{{.*}}= shl{{.*}}nsw{{.*}}i16
// CHECK_NSW-LABEL:         define <{{[0-9]*}} x i8> @shl_u8
// CHECK_NSW-NOT:             %{{.*}}= shl{{.*}}nsw{{.*}}i8

unmasked uint64 shl_u64(uint64 a, uint64 b) { return a << b; }

unmasked uint32 shl_u32(uint32 a, uint32 b) { return a << b; }

unmasked uint16 shl_u16(uint16 a, uint16 b) { return a << b; }

unmasked uint8 shl_u8(uint8 a, uint8 b) { return a << b; }

// Starting LLVM 19.0 there is a range attribute in @neg* functions:
// For example: define range(i8 -127, -128) <8 x i8>
// CHECK_NSW-LABEL:         define {{.*}}<{{[0-9]*}} x i64> @neg_64
// CHECK_NSW-COUNT-1:         %{{.*}}= sub{{.*}}nsw{{.*}}i64
// CHECK_NSW-LABEL:         define {{.*}}<{{[0-9]*}} x i32> @neg_32
// CHECK_NSW-COUNT-1:         %{{.*}}= sub{{.*}}nsw{{.*}}i32
// CHECK_NSW-LABEL:         define {{.*}}<{{[0-9]*}} x i16> @neg_16
// CHECK_NSW-COUNT-1:         %{{.*}}= sub{{.*}}nsw{{.*}}i16
// CHECK_NSW-LABEL:         define {{.*}}<{{[0-9]*}} x i8> @neg_8
// CHECK_NSW-COUNT-1:         %{{.*}}= sub{{.*}}nsw{{.*}}i8

unmasked int64 neg_64(int64 a) { return -a; }

unmasked int32 neg_32(int32 a) { return -a; }

unmasked int16 neg_16(int16 a) { return -a; }

unmasked int8 neg_8(int8 a) { return -a; }


// CHECK_NSW-LABEL:         define {{.*}} @ptrMath_64
// CHECK_NSW-NOT:             nsw
// CHECK_NSW-LABEL:         define {{.*}} @ptrMath_32
// CHECK_NSW-NOT:             nsw
// CHECK_NSW-LABEL:         define {{.*}} @ptrMath_16
// CHECK_NSW-NOT:             nsw
// CHECK_NSW-LABEL:         define {{.*}} @ptrMath_8
// CHECK_NSW-NOT:             nsw

unmasked uniform int64 * uniform ptrMath_64(uniform int64 * uniform a, uniform int64 k) { return &a[0] + k; }

unmasked uniform int32 * uniform ptrMath_32(uniform int32 * uniform a, uniform int32 k) { return &a[0] + k; }

unmasked uniform int16 * uniform ptrMath_16(uniform int16 * uniform a, uniform int16 k) { return &a[0] + k; }

unmasked uniform int8 * uniform ptrMath_8(uniform int8 * uniform a, uniform int8 k) { return &a[0] + k; }
