; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
; RUN: opt -mtriple=amdgcn-- -mcpu=gfx1030 -passes=instcombine -S < %s | FileCheck %s

; test unary

define float @hoist_fneg_f32(float %arg) {
; CHECK-LABEL: define float @hoist_fneg_f32(
; CHECK-SAME: float [[ARG:%.*]]) #[[ATTR0:[0-9]+]] {
; CHECK-NEXT:  [[BB:.*:]]
; CHECK-NEXT:    [[TMP0:%.*]] = call float @llvm.amdgcn.readfirstlane.f32(float [[ARG]])
; CHECK-NEXT:    [[RFL:%.*]] = fneg float [[TMP0]]
; CHECK-NEXT:    ret float [[RFL]]
;
bb:
  %val = fneg float %arg
  %rfl = call float @llvm.amdgcn.readfirstlane.f32(float %val)
  ret float %rfl
}

define double @hoist_fneg_f64(double %arg) {
; CHECK-LABEL: define double @hoist_fneg_f64(
; CHECK-SAME: double [[ARG:%.*]]) #[[ATTR0]] {
; CHECK-NEXT:  [[BB:.*:]]
; CHECK-NEXT:    [[TMP0:%.*]] = call double @llvm.amdgcn.readfirstlane.f64(double [[ARG]])
; CHECK-NEXT:    [[RFL:%.*]] = fneg double [[TMP0]]
; CHECK-NEXT:    ret double [[RFL]]
;
bb:
  %val = fneg double %arg
  %rfl = call double @llvm.amdgcn.readfirstlane.f64(double %val)
  ret double %rfl
}

; test casts

define i32 @hoist_trunc(i64 %arg) {
; CHECK-LABEL: define i32 @hoist_trunc(
; CHECK-SAME: i64 [[ARG:%.*]]) #[[ATTR0]] {
; CHECK-NEXT:  [[BB:.*:]]
; CHECK-NEXT:    [[RFL:%.*]] = call i64 @llvm.amdgcn.readfirstlane.i64(i64 [[ARG]])
; CHECK-NEXT:    [[TMP0:%.*]] = trunc i64 [[RFL]] to i32
; CHECK-NEXT:    ret i32 [[TMP0]]
;
bb:
  %val = trunc i64 %arg to i32
  %rfl = call i32 @llvm.amdgcn.readfirstlane.i32(i32 %val)
  ret i32 %rfl
}

define i64 @hoist_zext(i32 %arg) {
; CHECK-LABEL: define i64 @hoist_zext(
; CHECK-SAME: i32 [[ARG:%.*]]) #[[ATTR0]] {
; CHECK-NEXT:  [[BB:.*:]]
; CHECK-NEXT:    [[RFL:%.*]] = call i32 @llvm.amdgcn.readfirstlane.i32(i32 [[ARG]])
; CHECK-NEXT:    [[TMP0:%.*]] = zext i32 [[RFL]] to i64
; CHECK-NEXT:    ret i64 [[TMP0]]
;
bb:
  %val = zext i32 %arg to i64
  %rfl = call i64 @llvm.amdgcn.readfirstlane.i64(i64 %val)
  ret i64 %rfl
}

define i64 @hoist_sext(i32 %arg) {
; CHECK-LABEL: define i64 @hoist_sext(
; CHECK-SAME: i32 [[ARG:%.*]]) #[[ATTR0]] {
; CHECK-NEXT:  [[BB:.*:]]
; CHECK-NEXT:    [[RFL:%.*]] = call i32 @llvm.amdgcn.readfirstlane.i32(i32 [[ARG]])
; CHECK-NEXT:    [[TMP0:%.*]] = zext i32 [[RFL]] to i64
; CHECK-NEXT:    ret i64 [[TMP0]]
;
bb:
  %val = zext i32 %arg to i64
  %rfl = call i64 @llvm.amdgcn.readfirstlane.i64(i64 %val)
  ret i64 %rfl
}

define i32 @hoist_fptoui(float %arg) {
; CHECK-LABEL: define i32 @hoist_fptoui(
; CHECK-SAME: float [[ARG:%.*]]) #[[ATTR0]] {
; CHECK-NEXT:  [[BB:.*:]]
; CHECK-NEXT:    [[RFL:%.*]] = call float @llvm.amdgcn.readfirstlane.f32(float [[ARG]])
; CHECK-NEXT:    [[TMP0:%.*]] = fptoui float [[RFL]] to i32
; CHECK-NEXT:    ret i32 [[TMP0]]
;
bb:
  %val = fptoui float %arg to i32
  %rfl = call i32 @llvm.amdgcn.readfirstlane.i32(i32 %val)
  ret i32 %rfl
}

define i32 @hoist_fptosi(float %arg) {
; CHECK-LABEL: define i32 @hoist_fptosi(
; CHECK-SAME: float [[ARG:%.*]]) #[[ATTR0]] {
; CHECK-NEXT:  [[BB:.*:]]
; CHECK-NEXT:    [[RFL:%.*]] = call float @llvm.amdgcn.readfirstlane.f32(float [[ARG]])
; CHECK-NEXT:    [[TMP0:%.*]] = fptosi float [[RFL]] to i32
; CHECK-NEXT:    ret i32 [[TMP0]]
;
bb:
  %val = fptosi float %arg to i32
  %rfl = call i32 @llvm.amdgcn.readfirstlane.i32(i32 %val)
  ret i32 %rfl
}

define float @hoist_uitofp(i32 %arg) {
; CHECK-LABEL: define float @hoist_uitofp(
; CHECK-SAME: i32 [[ARG:%.*]]) #[[ATTR0]] {
; CHECK-NEXT:  [[BB:.*:]]
; CHECK-NEXT:    [[RFL:%.*]] = call i32 @llvm.amdgcn.readfirstlane.i32(i32 [[ARG]])
; CHECK-NEXT:    [[TMP0:%.*]] = uitofp i32 [[RFL]] to float
; CHECK-NEXT:    ret float [[TMP0]]
;
bb:
  %val = uitofp i32 %arg to float
  %rfl = call float @llvm.amdgcn.readfirstlane.f32(float %val)
  ret float %rfl
}

define float @hoist_sitofp(i32 %arg) {
; CHECK-LABEL: define float @hoist_sitofp(
; CHECK-SAME: i32 [[ARG:%.*]]) #[[ATTR0]] {
; CHECK-NEXT:  [[BB:.*:]]
; CHECK-NEXT:    [[RFL:%.*]] = call i32 @llvm.amdgcn.readfirstlane.i32(i32 [[ARG]])
; CHECK-NEXT:    [[TMP0:%.*]] = sitofp i32 [[RFL]] to float
; CHECK-NEXT:    ret float [[TMP0]]
;
bb:
  %val = sitofp i32 %arg to float
  %rfl = call float @llvm.amdgcn.readfirstlane.f32(float %val)
  ret float %rfl
}

define float @hoist_fptrunc(double %arg) {
; CHECK-LABEL: define float @hoist_fptrunc(
; CHECK-SAME: double [[ARG:%.*]]) #[[ATTR0]] {
; CHECK-NEXT:  [[BB:.*:]]
; CHECK-NEXT:    [[RFL:%.*]] = call double @llvm.amdgcn.readfirstlane.f64(double [[ARG]])
; CHECK-NEXT:    [[TMP0:%.*]] = fptrunc double [[RFL]] to float
; CHECK-NEXT:    ret float [[TMP0]]
;
bb:
  %val = fptrunc double %arg to float
  %rfl = call float @llvm.amdgcn.readfirstlane.f32(float %val)
  ret float %rfl
}

define float @hoist_fpext(half %arg) {
; CHECK-LABEL: define float @hoist_fpext(
; CHECK-SAME: half [[ARG:%.*]]) #[[ATTR0]] {
; CHECK-NEXT:  [[BB:.*:]]
; CHECK-NEXT:    [[RFL:%.*]] = call half @llvm.amdgcn.readfirstlane.f16(half [[ARG]])
; CHECK-NEXT:    [[TMP0:%.*]] = fpext half [[RFL]] to float
; CHECK-NEXT:    ret float [[TMP0]]
;
bb:
  %val = fpext half %arg to float
  %rfl = call float @llvm.amdgcn.readfirstlane.f32(float %val)
  ret float %rfl
}

define i64 @hoist_ptrtoint(ptr %arg) {
; CHECK-LABEL: define i64 @hoist_ptrtoint(
; CHECK-SAME: ptr [[ARG:%.*]]) #[[ATTR0]] {
; CHECK-NEXT:  [[BB:.*:]]
; CHECK-NEXT:    [[RFL:%.*]] = call ptr @llvm.amdgcn.readfirstlane.p0(ptr [[ARG]])
; CHECK-NEXT:    [[TMP0:%.*]] = ptrtoint ptr [[RFL]] to i64
; CHECK-NEXT:    ret i64 [[TMP0]]
;
bb:
  %val = ptrtoint ptr %arg to i64
  %rfl = call i64 @llvm.amdgcn.readfirstlane.i64(i64 %val)
  ret i64 %rfl
}

define ptr @hoist_inttoptr(i64 %arg) {
; CHECK-LABEL: define ptr @hoist_inttoptr(
; CHECK-SAME: i64 [[ARG:%.*]]) #[[ATTR0]] {
; CHECK-NEXT:  [[BB:.*:]]
; CHECK-NEXT:    [[RFL:%.*]] = call i64 @llvm.amdgcn.readfirstlane.i64(i64 [[ARG]])
; CHECK-NEXT:    [[TMP0:%.*]] = inttoptr i64 [[RFL]] to ptr
; CHECK-NEXT:    ret ptr [[TMP0]]
;
bb:
  %val = inttoptr i64 %arg to ptr
  %rfl = call ptr @llvm.amdgcn.readfirstlane.p0(ptr %val)
  ret ptr %rfl
}

define float @hoist_bitcast(i32 %arg) {
; CHECK-LABEL: define float @hoist_bitcast(
; CHECK-SAME: i32 [[ARG:%.*]]) #[[ATTR0]] {
; CHECK-NEXT:  [[BB:.*:]]
; CHECK-NEXT:    [[RFL:%.*]] = call i32 @llvm.amdgcn.readfirstlane.i32(i32 [[ARG]])
; CHECK-NEXT:    [[TMP0:%.*]] = bitcast i32 [[RFL]] to float
; CHECK-NEXT:    ret float [[TMP0]]
;
bb:
  %val = bitcast i32 %arg to float
  %rfl = call float @llvm.amdgcn.readfirstlane.f32(float %val)
  ret float %rfl
}

define ptr addrspace(1) @hoist_addrspacecast(ptr addrspace(0) %arg) {
; CHECK-LABEL: define ptr addrspace(1) @hoist_addrspacecast(
; CHECK-SAME: ptr [[ARG:%.*]]) #[[ATTR0]] {
; CHECK-NEXT:  [[BB:.*:]]
; CHECK-NEXT:    [[RFL:%.*]] = call ptr @llvm.amdgcn.readfirstlane.p0(ptr [[ARG]])
; CHECK-NEXT:    [[TMP0:%.*]] = addrspacecast ptr [[RFL]] to ptr addrspace(1)
; CHECK-NEXT:    ret ptr addrspace(1) [[TMP0]]
;
bb:
  %val = addrspacecast ptr addrspace(0) %arg to ptr addrspace(1)
  %rfl = call ptr addrspace(1) @llvm.amdgcn.readfirstlane.p1(ptr addrspace(1) %val)
  ret ptr addrspace(1) %rfl
}

; test binary i32

define i32 @hoist_add_i32(i32 %arg) {
; CHECK-LABEL: define i32 @hoist_add_i32(
; CHECK-SAME: i32 [[ARG:%.*]]) #[[ATTR0]] {
; CHECK-NEXT:  [[BB:.*:]]
; CHECK-NEXT:    [[TMP0:%.*]] = call i32 @llvm.amdgcn.readfirstlane.i32(i32 [[ARG]])
; CHECK-NEXT:    [[RFL:%.*]] = add i32 [[TMP0]], 16777215
; CHECK-NEXT:    ret i32 [[RFL]]
;
bb:
  %val = add i32 %arg, 16777215
  %rfl = call i32 @llvm.amdgcn.readfirstlane.i32(i32 %val)
  ret i32 %rfl
}

define float @hoist_fadd_f32(float %arg) {
; CHECK-LABEL: define float @hoist_fadd_f32(
; CHECK-SAME: float [[ARG:%.*]]) #[[ATTR0]] {
; CHECK-NEXT:  [[BB:.*:]]
; CHECK-NEXT:    [[TMP0:%.*]] = call float @llvm.amdgcn.readfirstlane.f32(float [[ARG]])
; CHECK-NEXT:    [[RFL:%.*]] = fadd float [[TMP0]], 1.280000e+02
; CHECK-NEXT:    ret float [[RFL]]
;
bb:
  %val = fadd float %arg, 128.0
  %rfl = call float @llvm.amdgcn.readfirstlane.f32(float %val)
  ret float %rfl
}

define i32 @hoist_sub_i32(i32 %arg) {
; CHECK-LABEL: define i32 @hoist_sub_i32(
; CHECK-SAME: i32 [[ARG:%.*]]) #[[ATTR0]] {
; CHECK-NEXT:  [[BB:.*:]]
; CHECK-NEXT:    [[TMP0:%.*]] = call i32 @llvm.amdgcn.readfirstlane.i32(i32 [[ARG]])
; CHECK-NEXT:    [[RFL:%.*]] = add i32 [[TMP0]], -16777215
; CHECK-NEXT:    ret i32 [[RFL]]
;
bb:
  %val = sub i32 %arg, 16777215
  %rfl = call i32 @llvm.amdgcn.readfirstlane.i32(i32 %val)
  ret i32 %rfl
}

define float @hoist_fsub_f32(float %arg) {
; CHECK-LABEL: define float @hoist_fsub_f32(
; CHECK-SAME: float [[ARG:%.*]]) #[[ATTR0]] {
; CHECK-NEXT:  [[BB:.*:]]
; CHECK-NEXT:    [[TMP0:%.*]] = call float @llvm.amdgcn.readfirstlane.f32(float [[ARG]])
; CHECK-NEXT:    [[RFL:%.*]] = fadd float [[TMP0]], -1.280000e+02
; CHECK-NEXT:    ret float [[RFL]]
;
bb:
  %val = fsub float %arg, 128.0
  %rfl = call float @llvm.amdgcn.readfirstlane.f32(float %val)
  ret float %rfl
}

define i32 @hoist_mul_i32(i32 %arg) {
; CHECK-LABEL: define i32 @hoist_mul_i32(
; CHECK-SAME: i32 [[ARG:%.*]]) #[[ATTR0]] {
; CHECK-NEXT:  [[BB:.*:]]
; CHECK-NEXT:    [[TMP0:%.*]] = call i32 @llvm.amdgcn.readfirstlane.i32(i32 [[ARG]])
; CHECK-NEXT:    [[RFL:%.*]] = mul i32 [[TMP0]], 16777215
; CHECK-NEXT:    ret i32 [[RFL]]
;
bb:
  %val = mul i32 %arg, 16777215
  %rfl = call i32 @llvm.amdgcn.readfirstlane.i32(i32 %val)
  ret i32 %rfl
}

define float @hoist_fmul_f32(float %arg) {
; CHECK-LABEL: define float @hoist_fmul_f32(
; CHECK-SAME: float [[ARG:%.*]]) #[[ATTR0]] {
; CHECK-NEXT:  [[BB:.*:]]
; CHECK-NEXT:    [[TMP0:%.*]] = call float @llvm.amdgcn.readfirstlane.f32(float [[ARG]])
; CHECK-NEXT:    [[RFL:%.*]] = fmul float [[TMP0]], 1.280000e+02
; CHECK-NEXT:    ret float [[RFL]]
;
bb:
  %val = fmul float %arg, 128.0
  %rfl = call float @llvm.amdgcn.readfirstlane.f32(float %val)
  ret float %rfl
}

define i32 @hoist_udiv_i32(i32 %arg) {
; CHECK-LABEL: define i32 @hoist_udiv_i32(
; CHECK-SAME: i32 [[ARG:%.*]]) #[[ATTR0]] {
; CHECK-NEXT:  [[BB:.*:]]
; CHECK-NEXT:    [[TMP0:%.*]] = call i32 @llvm.amdgcn.readfirstlane.i32(i32 [[ARG]])
; CHECK-NEXT:    [[RFL:%.*]] = udiv i32 [[TMP0]], 16777215
; CHECK-NEXT:    ret i32 [[RFL]]
;
bb:
  %val = udiv i32 %arg, 16777215
  %rfl = call i32 @llvm.amdgcn.readfirstlane.i32(i32 %val)
  ret i32 %rfl
}

define i32 @hoist_sdiv_i32(i32 %arg) {
; CHECK-LABEL: define i32 @hoist_sdiv_i32(
; CHECK-SAME: i32 [[ARG:%.*]]) #[[ATTR0]] {
; CHECK-NEXT:  [[BB:.*:]]
; CHECK-NEXT:    [[TMP0:%.*]] = call i32 @llvm.amdgcn.readfirstlane.i32(i32 [[ARG]])
; CHECK-NEXT:    [[RFL:%.*]] = sdiv i32 [[TMP0]], 16777215
; CHECK-NEXT:    ret i32 [[RFL]]
;
bb:
  %val = sdiv i32 %arg, 16777215
  %rfl = call i32 @llvm.amdgcn.readfirstlane.i32(i32 %val)
  ret i32 %rfl
}

define float @hoist_fdiv_f32(float %arg) {
; CHECK-LABEL: define float @hoist_fdiv_f32(
; CHECK-SAME: float [[ARG:%.*]]) #[[ATTR0]] {
; CHECK-NEXT:  [[BB:.*:]]
; CHECK-NEXT:    [[TMP0:%.*]] = call float @llvm.amdgcn.readfirstlane.f32(float [[ARG]])
; CHECK-NEXT:    [[RFL:%.*]] = fmul float [[TMP0]], 7.812500e-03
; CHECK-NEXT:    ret float [[RFL]]
;
bb:
  %val = fdiv float %arg, 128.0
  %rfl = call float @llvm.amdgcn.readfirstlane.f32(float %val)
  ret float %rfl
}

define i32 @hoist_urem_i32(i32 %arg) {
; CHECK-LABEL: define i32 @hoist_urem_i32(
; CHECK-SAME: i32 [[ARG:%.*]]) #[[ATTR0]] {
; CHECK-NEXT:  [[BB:.*:]]
; CHECK-NEXT:    [[TMP0:%.*]] = call i32 @llvm.amdgcn.readfirstlane.i32(i32 [[ARG]])
; CHECK-NEXT:    [[RFL:%.*]] = urem i32 [[TMP0]], 16777215
; CHECK-NEXT:    ret i32 [[RFL]]
;
bb:
  %val = urem i32 %arg, 16777215
  %rfl = call i32 @llvm.amdgcn.readfirstlane.i32(i32 %val)
  ret i32 %rfl
}

define i32 @hoist_srem_i32(i32 %arg) {
; CHECK-LABEL: define i32 @hoist_srem_i32(
; CHECK-SAME: i32 [[ARG:%.*]]) #[[ATTR0]] {
; CHECK-NEXT:  [[BB:.*:]]
; CHECK-NEXT:    [[TMP0:%.*]] = call i32 @llvm.amdgcn.readfirstlane.i32(i32 [[ARG]])
; CHECK-NEXT:    [[RFL:%.*]] = srem i32 [[TMP0]], 16777215
; CHECK-NEXT:    ret i32 [[RFL]]
;
bb:
  %val = srem i32 %arg, 16777215
  %rfl = call i32 @llvm.amdgcn.readfirstlane.i32(i32 %val)
  ret i32 %rfl
}

define float @hoist_frem_f32(float %arg) {
; CHECK-LABEL: define float @hoist_frem_f32(
; CHECK-SAME: float [[ARG:%.*]]) #[[ATTR0]] {
; CHECK-NEXT:  [[BB:.*:]]
; CHECK-NEXT:    [[TMP0:%.*]] = call float @llvm.amdgcn.readfirstlane.f32(float [[ARG]])
; CHECK-NEXT:    [[RFL:%.*]] = frem float [[TMP0]], 1.280000e+02
; CHECK-NEXT:    ret float [[RFL]]
;
bb:
  %val = frem float %arg, 128.0
  %rfl = call float @llvm.amdgcn.readfirstlane.f32(float %val)
  ret float %rfl
}

define i32 @hoist_shl_i32(i32 %arg) {
; CHECK-LABEL: define i32 @hoist_shl_i32(
; CHECK-SAME: i32 [[ARG:%.*]]) #[[ATTR0]] {
; CHECK-NEXT:  [[BB:.*:]]
; CHECK-NEXT:    [[TMP0:%.*]] = call i32 @llvm.amdgcn.readfirstlane.i32(i32 [[ARG]])
; CHECK-NEXT:    [[RFL:%.*]] = shl i32 [[TMP0]], 4
; CHECK-NEXT:    ret i32 [[RFL]]
;
bb:
  %val = shl i32 %arg, 4
  %rfl = call i32 @llvm.amdgcn.readfirstlane.i32(i32 %val)
  ret i32 %rfl
}

define i32 @hoist_lshr_i32(i32 %arg) {
; CHECK-LABEL: define i32 @hoist_lshr_i32(
; CHECK-SAME: i32 [[ARG:%.*]]) #[[ATTR0]] {
; CHECK-NEXT:  [[BB:.*:]]
; CHECK-NEXT:    [[TMP0:%.*]] = call i32 @llvm.amdgcn.readfirstlane.i32(i32 [[ARG]])
; CHECK-NEXT:    [[RFL:%.*]] = lshr i32 [[TMP0]], 4
; CHECK-NEXT:    ret i32 [[RFL]]
;
bb:
  %val = lshr i32 %arg, 4
  %rfl = call i32 @llvm.amdgcn.readfirstlane.i32(i32 %val)
  ret i32 %rfl
}

define i32 @hoist_ashr_i32(i32 %arg) {
; CHECK-LABEL: define i32 @hoist_ashr_i32(
; CHECK-SAME: i32 [[ARG:%.*]]) #[[ATTR0]] {
; CHECK-NEXT:  [[BB:.*:]]
; CHECK-NEXT:    [[TMP0:%.*]] = call i32 @llvm.amdgcn.readfirstlane.i32(i32 [[ARG]])
; CHECK-NEXT:    [[RFL:%.*]] = ashr i32 [[TMP0]], 4
; CHECK-NEXT:    ret i32 [[RFL]]
;
bb:
  %val = ashr i32 %arg, 4
  %rfl = call i32 @llvm.amdgcn.readfirstlane.i32(i32 %val)
  ret i32 %rfl
}


define i32 @hoist_and_i32(i32 %arg) {
; CHECK-LABEL: define i32 @hoist_and_i32(
; CHECK-SAME: i32 [[ARG:%.*]]) #[[ATTR0]] {
; CHECK-NEXT:  [[BB:.*:]]
; CHECK-NEXT:    [[TMP0:%.*]] = call i32 @llvm.amdgcn.readfirstlane.i32(i32 [[ARG]])
; CHECK-NEXT:    [[RFL:%.*]] = and i32 [[TMP0]], 16777215
; CHECK-NEXT:    ret i32 [[RFL]]
;
bb:
  %val = and i32 %arg, 16777215
  %rfl = call i32 @llvm.amdgcn.readfirstlane.i32(i32 %val)
  ret i32 %rfl
}

define i32 @hoist_or_i32(i32 %arg) {
; CHECK-LABEL: define i32 @hoist_or_i32(
; CHECK-SAME: i32 [[ARG:%.*]]) #[[ATTR0]] {
; CHECK-NEXT:  [[BB:.*:]]
; CHECK-NEXT:    [[TMP0:%.*]] = call i32 @llvm.amdgcn.readfirstlane.i32(i32 [[ARG]])
; CHECK-NEXT:    [[RFL:%.*]] = or i32 [[TMP0]], 16777215
; CHECK-NEXT:    ret i32 [[RFL]]
;
bb:
  %val = or i32 %arg, 16777215
  %rfl = call i32 @llvm.amdgcn.readfirstlane.i32(i32 %val)
  ret i32 %rfl
}

define i32 @hoist_xor_i32(i32 %arg) {
; CHECK-LABEL: define i32 @hoist_xor_i32(
; CHECK-SAME: i32 [[ARG:%.*]]) #[[ATTR0]] {
; CHECK-NEXT:  [[BB:.*:]]
; CHECK-NEXT:    [[TMP0:%.*]] = call i32 @llvm.amdgcn.readfirstlane.i32(i32 [[ARG]])
; CHECK-NEXT:    [[RFL:%.*]] = xor i32 [[TMP0]], 16777215
; CHECK-NEXT:    ret i32 [[RFL]]
;
bb:
  %val = xor i32 %arg, 16777215
  %rfl = call i32 @llvm.amdgcn.readfirstlane.i32(i32 %val)
  ret i32 %rfl
}

; test binary i64

define i64 @hoist_and_i64(i64 %arg) {
; CHECK-LABEL: define i64 @hoist_and_i64(
; CHECK-SAME: i64 [[ARG:%.*]]) #[[ATTR0]] {
; CHECK-NEXT:  [[BB:.*:]]
; CHECK-NEXT:    [[TMP0:%.*]] = call i64 @llvm.amdgcn.readfirstlane.i64(i64 [[ARG]])
; CHECK-NEXT:    [[RFL:%.*]] = and i64 [[TMP0]], 16777215
; CHECK-NEXT:    ret i64 [[RFL]]
;
bb:
  %val = and i64 %arg, 16777215
  %rfl = call i64 @llvm.amdgcn.readfirstlane.i32(i64 %val)
  ret i64 %rfl
}

define double @hoist_fadd_f64(double %arg) {
; CHECK-LABEL: define double @hoist_fadd_f64(
; CHECK-SAME: double [[ARG:%.*]]) #[[ATTR0]] {
; CHECK-NEXT:  [[BB:.*:]]
; CHECK-NEXT:    [[TMP0:%.*]] = call double @llvm.amdgcn.readfirstlane.f64(double [[ARG]])
; CHECK-NEXT:    [[RFL:%.*]] = fadd double [[TMP0]], 1.280000e+02
; CHECK-NEXT:    ret double [[RFL]]
;
bb:
  %val = fadd double %arg, 128.0
  %rfl = call double @llvm.amdgcn.readfirstlane.f64(double %val)
  ret double %rfl
}

; test constant on LHS

define i32 @hoist_sub_i32_lhs(i32 %arg) {
; CHECK-LABEL: define i32 @hoist_sub_i32_lhs(
; CHECK-SAME: i32 [[ARG:%.*]]) #[[ATTR0]] {
; CHECK-NEXT:  [[BB:.*:]]
; CHECK-NEXT:    [[TMP0:%.*]] = call i32 @llvm.amdgcn.readfirstlane.i32(i32 [[ARG]])
; CHECK-NEXT:    [[RFL:%.*]] = sub i32 16777215, [[TMP0]]
; CHECK-NEXT:    ret i32 [[RFL]]
;
bb:
  %val = sub i32 16777215, %arg
  %rfl = call i32 @llvm.amdgcn.readfirstlane.i32(i32 %val)
  ret i32 %rfl
}

define float @hoist_fsub_f32_lhs(float %arg) {
; CHECK-LABEL: define float @hoist_fsub_f32_lhs(
; CHECK-SAME: float [[ARG:%.*]]) #[[ATTR0]] {
; CHECK-NEXT:  [[BB:.*:]]
; CHECK-NEXT:    [[TMP0:%.*]] = call float @llvm.amdgcn.readfirstlane.f32(float [[ARG]])
; CHECK-NEXT:    [[RFL:%.*]] = fsub float 1.280000e+02, [[TMP0]]
; CHECK-NEXT:    ret float [[RFL]]
;
bb:
  %val = fsub float 128.0, %arg
  %rfl = call float @llvm.amdgcn.readfirstlane.f32(float %val)
  ret float %rfl
}

; test other operand is trivially uniform

define i32 @hoist_add_i32_trivially_uniform_rhs(i32 %arg, i32 %v.other) {
; CHECK-LABEL: define i32 @hoist_add_i32_trivially_uniform_rhs(
; CHECK-SAME: i32 [[ARG:%.*]], i32 [[V_OTHER:%.*]]) #[[ATTR0]] {
; CHECK-NEXT:  [[BB:.*:]]
; CHECK-NEXT:    [[OTHER:%.*]] = call i32 @llvm.amdgcn.readfirstlane.i32(i32 [[V_OTHER]])
; CHECK-NEXT:    [[TMP0:%.*]] = call i32 @llvm.amdgcn.readfirstlane.i32(i32 [[ARG]])
; CHECK-NEXT:    [[RFL:%.*]] = add i32 [[TMP0]], [[OTHER]]
; CHECK-NEXT:    ret i32 [[RFL]]
;
bb:
  %other = call i32 @llvm.amdgcn.readfirstlane.i32(i32 %v.other)
  %val = add i32 %arg, %other
  %rfl = call i32 @llvm.amdgcn.readfirstlane.i32(i32 %val)
  ret i32 %rfl
}

define i32 @hoist_add_i32_trivially_uniform_lhs(i32 %arg, i32 %v.other) {
; CHECK-LABEL: define i32 @hoist_add_i32_trivially_uniform_lhs(
; CHECK-SAME: i32 [[ARG:%.*]], i32 [[V_OTHER:%.*]]) #[[ATTR0]] {
; CHECK-NEXT:  [[BB:.*:]]
; CHECK-NEXT:    [[OTHER:%.*]] = call i32 @llvm.amdgcn.readfirstlane.i32(i32 [[V_OTHER]])
; CHECK-NEXT:    [[TMP0:%.*]] = call i32 @llvm.amdgcn.readfirstlane.i32(i32 [[ARG]])
; CHECK-NEXT:    [[RFL:%.*]] = sub i32 [[OTHER]], [[TMP0]]
; CHECK-NEXT:    ret i32 [[RFL]]
;
bb:
  %other = call i32 @llvm.amdgcn.readfirstlane.i32(i32 %v.other)
  %val = sub i32 %other, %arg
  %rfl = call i32 @llvm.amdgcn.readfirstlane.i32(i32 %val)
  ret i32 %rfl
}

; test multiple iterations

define i32 @hoist_multiple_times(i32 %arg) {
; CHECK-LABEL: define i32 @hoist_multiple_times(
; CHECK-SAME: i32 [[ARG:%.*]]) #[[ATTR0]] {
; CHECK-NEXT:  [[BB:.*:]]
; CHECK-NEXT:    [[TMP0:%.*]] = call i32 @llvm.amdgcn.readfirstlane.i32(i32 [[ARG]])
; CHECK-NEXT:    [[TMP1:%.*]] = shl i32 [[TMP0]], 2
; CHECK-NEXT:    [[TMP2:%.*]] = sub i32 16777215, [[TMP1]]
; CHECK-NEXT:    [[TMP3:%.*]] = xor i32 [[TMP2]], 4242
; CHECK-NEXT:    [[RFL:%.*]] = add i32 [[TMP3]], 6
; CHECK-NEXT:    ret i32 [[RFL]]
;
bb:
  %val.0 = shl i32 %arg, 2
  %val.1 = sub i32 16777215, %val.0
  %val.2 = xor i32 %val.1, 4242
  %val.3 = add i32 %val.2, 6
  %rfl = call i32 @llvm.amdgcn.readfirstlane.i32(i32 %val.3)
  ret i32 %rfl
}

; test cases where hoisting isn't possible

define i32 @cross_block_hoisting(i1 %cond, i32 %arg) {
; CHECK-LABEL: define i32 @cross_block_hoisting(
; CHECK-SAME: i1 [[COND:%.*]], i32 [[ARG:%.*]]) #[[ATTR0]] {
; CHECK-NEXT:  [[BB:.*]]:
; CHECK-NEXT:    [[VAL:%.*]] = add i32 [[ARG]], 16777215
; CHECK-NEXT:    br i1 [[COND]], label %[[THEN:.*]], label %[[END:.*]]
; CHECK:       [[THEN]]:
; CHECK-NEXT:    [[RFL:%.*]] = call i32 @llvm.amdgcn.readfirstlane.i32(i32 [[VAL]])
; CHECK-NEXT:    br label %[[END]]
; CHECK:       [[END]]:
; CHECK-NEXT:    [[RES:%.*]] = phi i32 [ [[RFL]], %[[THEN]] ], [ [[VAL]], %[[BB]] ]
; CHECK-NEXT:    ret i32 [[RES]]
;
bb:
  %val = add i32 %arg, 16777215
  br i1 %cond, label %then, label %end

then:
  %rfl = call i32 @llvm.amdgcn.readfirstlane.i32(i32 %val)
  br label %end

end:
  %res = phi i32 [%rfl, %then], [%val, %bb]
  ret i32 %res
}

define i32 @operand_is_instr(i32 %arg, ptr %src) {
; CHECK-LABEL: define i32 @operand_is_instr(
; CHECK-SAME: i32 [[ARG:%.*]], ptr [[SRC:%.*]]) #[[ATTR0]] {
; CHECK-NEXT:  [[BB:.*:]]
; CHECK-NEXT:    [[OTHER:%.*]] = load i32, ptr [[SRC]], align 4
; CHECK-NEXT:    [[VAL:%.*]] = add i32 [[ARG]], [[OTHER]]
; CHECK-NEXT:    [[RFL:%.*]] = call i32 @llvm.amdgcn.readfirstlane.i32(i32 [[VAL]])
; CHECK-NEXT:    ret i32 [[RFL]]
;
bb:
  %other = load i32, ptr %src
  %val = add i32 %arg, %other
  %rfl = call i32 @llvm.amdgcn.readfirstlane.i32(i32 %val)
  ret i32 %rfl
}

define i32 @operand_is_arg(i32 %arg, i32 %other) {
; CHECK-LABEL: define i32 @operand_is_arg(
; CHECK-SAME: i32 [[ARG:%.*]], i32 [[OTHER:%.*]]) #[[ATTR0]] {
; CHECK-NEXT:  [[BB:.*:]]
; CHECK-NEXT:    [[VAL:%.*]] = add i32 [[ARG]], [[OTHER]]
; CHECK-NEXT:    [[RFL:%.*]] = call i32 @llvm.amdgcn.readfirstlane.i32(i32 [[VAL]])
; CHECK-NEXT:    ret i32 [[RFL]]
;
bb:
  %val = add i32 %arg, %other
  %rfl = call i32 @llvm.amdgcn.readfirstlane.i32(i32 %val)
  ret i32 %rfl
}

; test that convergence tokens are preserved

define i32 @hoist_preserves_convergence_token(i1 %cond, i32 %arg) convergent {
; CHECK-LABEL: define i32 @hoist_preserves_convergence_token(
; CHECK-SAME: i1 [[COND:%.*]], i32 [[ARG:%.*]]) #[[ATTR1:[0-9]+]] {
; CHECK-NEXT:  [[BB:.*]]:
; CHECK-NEXT:    [[ENTRY:%.*]] = call token @llvm.experimental.convergence.entry()
; CHECK-NEXT:    br i1 [[COND]], label %[[THEN:.*]], label %[[END:.*]]
; CHECK:       [[THEN]]:
; CHECK-NEXT:    [[TMP0:%.*]] = call i32 @llvm.amdgcn.readfirstlane.i32(i32 [[ARG]]) [ "convergencectrl"(token [[ENTRY]]) ]
; CHECK-NEXT:    [[RFL:%.*]] = add i32 [[TMP0]], 16777215
; CHECK-NEXT:    br label %[[END]]
; CHECK:       [[END]]:
; CHECK-NEXT:    [[RES:%.*]] = phi i32 [ [[RFL]], %[[THEN]] ], [ [[ARG]], %[[BB]] ]
; CHECK-NEXT:    ret i32 [[RES]]
;
bb:
  %entry = call token @llvm.experimental.convergence.entry()
  br i1 %cond, label %then, label %end

then:
  %val = add i32 %arg, 16777215
  %rfl = call i32 @llvm.amdgcn.readfirstlane.i32(i32 %val) [ "convergencectrl"(token %entry)]
  br label %end

end:
  %res = phi i32 [%rfl, %then], [%arg, %bb]
  ret i32 %res
}
