; This is a test of C-level conversion operations that clang lowers
; into pairs of shifts.

; RUN: %if --need=target_X8632 --command %p2i --filetype=obj --disassemble \
; RUN:   --target x8632 -i %s --args -O2 \
; RUN:   | %if --need=target_X8632 --command FileCheck %s

; RUN: %if --need=target_X8632 --command %p2i --filetype=obj --disassemble \
; RUN:   --target x8632 -i %s --args -Om1 \
; RUN:   | %if --need=target_X8632 --command FileCheck %s

; RUN: %if --need=target_ARM32 \
; RUN:   --command %p2i --filetype=obj \
; RUN:   --disassemble --target arm32 -i %s --args -O2 \
; RUN:   | %if --need=target_ARM32 \
; RUN:   --command FileCheck --check-prefix ARM32 %s

; RUN: %if --need=target_ARM32 \
; RUN:   --command %p2i --filetype=obj \
; RUN:   --disassemble --target arm32 -i %s --args -Om1 \
; RUN:   | %if --need=target_ARM32 \
; RUN:   --command FileCheck --check-prefix ARM32 %s

; RUN: %if --need=target_MIPS32 --need=allow_dump \
; RUN:   --command %p2i --filetype=asm --assemble \
; RUN:   --disassemble --target mips32 -i %s --args -O2 \
; RUN:   | %if --need=target_MIPS32 --need=allow_dump \
; RUN:   --command FileCheck --check-prefix MIPS32-O2 --check-prefix MIPS32 %s

; RUN: %if --need=target_MIPS32 --need=allow_dump \
; RUN:   --command %p2i --filetype=asm --assemble \
; RUN:   --disassemble --target mips32 -i %s --args -Om1 \
; RUN:   | %if --need=target_MIPS32 --need=allow_dump \
; RUN:   --command FileCheck --check-prefix MIPS32-OM1 --check-prefix MIPS32 %s

@i1 = internal global [4 x i8] zeroinitializer, align 4
@i2 = internal global [4 x i8] zeroinitializer, align 4
@u1 = internal global [4 x i8] zeroinitializer, align 4

define internal void @conv1() {
entry:
  %__0 = bitcast [4 x i8]* @u1 to i32*
  %v0 = load i32, i32* %__0, align 1
  %sext = shl i32 %v0, 24
  %v1 = ashr i32 %sext, 24
  %__4 = bitcast [4 x i8]* @i1 to i32*
  store i32 %v1, i32* %__4, align 1
  ret void
}
; CHECK-LABEL: conv1
; CHECK: shl {{.*}},0x18
; CHECK: sar {{.*}},0x18

; ARM32-LABEL: conv1
; ARM32: lsl {{.*}}, #24
; ARM32: asr {{.*}}, #24

define internal void @conv2() {
entry:
  %__0 = bitcast [4 x i8]* @u1 to i32*
  %v0 = load i32, i32* %__0, align 1
  %sext1 = shl i32 %v0, 16
  %v1 = lshr i32 %sext1, 16
  %__4 = bitcast [4 x i8]* @i2 to i32*
  store i32 %v1, i32* %__4, align 1
  ret void
}
; CHECK-LABEL: conv2
; CHECK: shl {{.*}},0x10
; CHECK: shr {{.*}},0x10

; ARM32-LABEL: conv2
; ARM32: lsl {{.*}}, #16
; ARM32: lsr {{.*}}, #16

define internal i32 @shlImmLarge(i32 %val) {
entry:
  %result = shl i32 %val, 257
  ret i32 %result
}
; CHECK-LABEL: shlImmLarge
; CHECK: shl {{.*}},0x1

; MIPS32-LABEL: shlImmLarge
; MIPS32: sll

define internal i32 @shlImmNeg(i32 %val) {
entry:
  %result = shl i32 %val, -1
  ret i32 %result
}
; CHECK-LABEL: shlImmNeg
; CHECK: shl {{.*}},0xff

; MIPS32-LABEL: shlImmNeg
; MIPS32: sll

define internal i32 @lshrImmLarge(i32 %val) {
entry:
  %result = lshr i32 %val, 257
  ret i32 %result
}
; CHECK-LABEL: lshrImmLarge
; CHECK: shr {{.*}},0x1

; MIPS32-LABEL: lshrImmLarge
; MIPS32: srl

define internal i32 @lshrImmNeg(i32 %val) {
entry:
  %result = lshr i32 %val, -1
  ret i32 %result
}
; CHECK-LABEL: lshrImmNeg
; CHECK: shr {{.*}},0xff

; MIPS32-LABEL: lshrImmNeg
; MIPS32: srl

define internal i32 @ashrImmLarge(i32 %val) {
entry:
  %result = ashr i32 %val, 257
  ret i32 %result
}
; CHECK-LABEL: ashrImmLarge
; CHECK: sar {{.*}},0x1

; MIPS32-LABEL: ashrImmLarge
; MIPS32: sra

define internal i32 @ashrImmNeg(i32 %val) {
entry:
  %result = ashr i32 %val, -1
  ret i32 %result
}
; CHECK-LABEL: ashrImmNeg
; CHECK: sar {{.*}},0xff

; MIPS32-LABEL: ashrImmNeg
; MIPS32: sra

define internal i64 @shlImm64One(i64 %val) {
entry:
  %result = shl i64 %val, 1
  ret i64 %result
}
; CHECK-LABEL: shlImm64One
; CHECK: shl {{.*}},1
; MIPS32-LABEL: shlImm64One
; MIPS32: addu	[[T_LO:.*]],[[VAL_LO:.*]],[[VAL_LO]]
; MIPS32: sltu	[[T1:.*]],[[T_LO]],[[VAL_LO]]
; MIPS32: addu	[[T2:.*]],[[T1]],[[VAL_HI:.*]]
; MIPS32: addu	{{.*}},[[VAL_HI]],[[T2]]

define internal i64 @shlImm64LessThan32(i64 %val) {
entry:
  %result = shl i64 %val, 4
  ret i64 %result
}
; CHECK-LABEL: shlImm64LessThan32
; CHECK: shl {{.*}},0x4
; MIPS32-LABEL: shlImm64LessThan32
; MIPS32: srl	[[T1:.*]],[[VAL_LO:.*]],0x1c
; MIPS32: sll	[[T2:.*]],{{.*}},0x4
; MIPS32: or	{{.*}},[[T1]],[[T2]]
; MIPS32: sll	{{.*}},[[VAL_LO]],0x4

define internal i64 @shlImm64Equal32(i64 %val) {
entry:
  %result = shl i64 %val, 32
  ret i64 %result
}
; CHECK-LABEL: shlImm64Equal32
; CHECK-NOT: shl
; MIPS32-LABEL: shlImm64Equal32
; MIPS32: li	{{.*}},0
; MIPS32-O2:	move
; MIPS32-OM1:	sw
; MIPS32-OM1:	lw

define internal i64 @shlImm64GreaterThan32(i64 %val) {
entry:
  %result = shl i64 %val, 40
  ret i64 %result
}
; CHECK-LABEL: shlImm64GreaterThan32
; CHECK: shl {{.*}},0x8
; MIPS32-LABEL: shlImm64GreaterThan32
; MIPS32: sll	{{.*}},{{.*}},0x8
; MIPS32: li	{{.*}},0

define internal i64 @lshrImm64One(i64 %val) {
entry:
  %result = lshr i64 %val, 1
  ret i64 %result
}
; CHECK-LABEL: lshrImm64One
; CHECK: shr {{.*}},1
; MIPS32-LABEL: lshrImm64One
; MIPS32: sll	[[T1:.*]],[[VAL_HI:.*]],0x1f
; MIPS32: srl	[[T2:.*]],{{.*}},0x1
; MIPS32: or	{{.*}},[[T1]],[[T2]]
; MIPS32: srl	{{.*}},[[VAL_HI]],0x1

define internal i64 @lshrImm64LessThan32(i64 %val) {
entry:
  %result = lshr i64 %val, 4
  ret i64 %result
}
; CHECK-LABEL: lshrImm64LessThan32
; CHECK: shrd {{.*}},0x4
; CHECK: shr {{.*}},0x4
; MIPS32-LABEL: lshrImm64LessThan32
; MIPS32: sll	[[T1:.*]],[[VAL_HI:.*]],0x1c
; MIPS32: srl	[[T2:.*]],{{.*}},0x4
; MIPS32: or	{{.*}},[[T1]],[[T2]]
; MIPS32: srl	{{.*}},[[VAL_HI]],0x4

define internal i64 @lshrImm64Equal32(i64 %val) {
entry:
  %result = lshr i64 %val, 32
  ret i64 %result
}
; CHECK-LABEL: lshrImm64Equal32
; CHECK-NOT: shr
; MIPS32-LABEL: lshrImm64Equal32
; MIPS32: li	{{.*}},0
; MIPS32-O2: move
; MIPS32-OM1: sw
; MIPS32-OM1: lw

define internal i64 @lshrImm64GreaterThan32(i64 %val) {
entry:
  %result = lshr i64 %val, 40
  ret i64 %result
}
; CHECK-LABEL: lshrImm64GreaterThan32
; CHECK-NOT: shrd
; CHECK: shr {{.*}},0x8
; MIPS32-LABEL: lshrImm64GreaterThan32
; MIPS32: srl	{{.*}},{{.*}},0x8
; MIPS32: li	{{.*}},0

define internal i64 @ashrImm64One(i64 %val) {
entry:
  %result = ashr i64 %val, 1
  ret i64 %result
}
; CHECK-LABEL: ashrImm64One
; CHECK: shrd {{.*}},0x1
; CHECK: sar {{.*}},1
; MIPS32-LABEL: ashrImm64One
; MIPS32: sll	[[T1:.*]],[[VAL_HI:.*]],0x1f
; MIPS32: srl	[[T2:.*]],{{.*}},0x1
; MIPS32: or	{{.*}},[[T1]],[[T2]]
; MIPS32: sra	{{.*}},[[VAL_HI]],0x1

define internal i64 @ashrImm64LessThan32(i64 %val) {
entry:
  %result = ashr i64 %val, 4
  ret i64 %result
}
; CHECK-LABEL: ashrImm64LessThan32
; CHECK: shrd {{.*}},0x4
; CHECK: sar {{.*}},0x4
; MIPS32-LABEL: ashrImm64LessThan32
; MIPS32: sll   [[T1:.*]],[[VAL_HI:.*]],0x1c
; MIPS32: srl   [[T2:.*]],{{.*}},0x4
; MIPS32: or    {{.*}},[[T1]],[[T2]]
; MIPS32: sra   {{.*}},[[VAL_HI]],0x4

define internal i64 @ashrImm64Equal32(i64 %val) {
entry:
  %result = ashr i64 %val, 32
  ret i64 %result
}
; CHECK-LABEL: ashrImm64Equal32
; CHECK: sar {{.*}},0x1f
; CHECK-NOT: shrd
; MIPS32-LABEL: ashrImm64Equal32
; MIPS32: sra	{{.*}},[[VAL_HI:.*]],0x1f
; MIPS32-O2: move	{{.*}},[[VAL_HI]]
; MIPS32-OM1:	sw [[VAL_HI]],{{.*}}
; MIPS32-OM1:   lw {{.*}},{{.*}}

define internal i64 @ashrImm64GreaterThan32(i64 %val) {
entry:
  %result = ashr i64 %val, 40
  ret i64 %result
}
; CHECK-LABEL: ashrImm64GreaterThan32
; CHECK: sar {{.*}},0x1f
; CHECK: shrd {{.*}},0x8
; MIPS32-LABEL: ashrImm64GreaterThan32
; MIPS32: sra	{{.*}},[[VAL_HI:.*]],0x8
; MIPS32: sra	{{.*}},[[VAL_HI]],0x1f