; 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 }