| ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5 |
| ; RUN: llc -mtriple=amdgcn -mcpu=gfx950 < %s | FileCheck %s |
| |
| ; This is a regression test for a bug where SIFixSGPRCopies would incorrectly |
| ; keep the value as VGPR when it should be converted to SGPR for inline asm. |
| |
| define <4 x float> @test_sgpr_constraint_bug(ptr addrspace(5) %buf_desc_ptr) { |
| ; CHECK-LABEL: test_sgpr_constraint_bug: |
| ; CHECK: ; %bb.0: |
| ; CHECK-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) |
| ; CHECK-NEXT: scratch_load_dwordx4 v[4:7], v0, off |
| ; CHECK-NEXT: v_mov_b32_e32 v0, 0 |
| ; CHECK-NEXT: v_mov_b32_e32 v1, v0 |
| ; CHECK-NEXT: v_mov_b32_e32 v2, v0 |
| ; CHECK-NEXT: v_mov_b32_e32 v3, v0 |
| ; CHECK-NEXT: s_mov_b64 s[4:5], exec |
| ; CHECK-NEXT: v_mov_b32_e32 v8, 1 |
| ; CHECK-NEXT: s_waitcnt vmcnt(0) |
| ; CHECK-NEXT: v_readfirstlane_b32 s3, v7 |
| ; CHECK-NEXT: v_readfirstlane_b32 s2, v6 |
| ; CHECK-NEXT: v_readfirstlane_b32 s1, v5 |
| ; CHECK-NEXT: v_readfirstlane_b32 s0, v4 |
| ; CHECK-NEXT: ;;#ASMSTART |
| ; CHECK-NEXT: v_cmpx_le_u32 exec, 1, v8 |
| ; CHECK-NEXT: buffer_load_dwordx4 v[0:3], v0, s[0:3], 0 offen offset:0 |
| ; CHECK-NEXT: s_mov_b64 exec s[4:5] |
| ; CHECK-NEXT: ;;#ASMEND |
| ; CHECK-NEXT: s_setpc_b64 s[30:31] |
| %rsrc = load <4 x i32>, ptr addrspace(5) %buf_desc_ptr, align 16 |
| |
| %exec = call i64 @llvm.amdgcn.ballot.i64(i1 true) |
| |
| %result = call <4 x float> asm sideeffect |
| "v_cmpx_le_u32 exec, 1, $4\0Abuffer_load_dwordx4 $0, $1, $2, 0 offen offset:$3\0As_mov_b64 exec $5", |
| "=v,v,s,n,v,s,0,~{memory}" |
| (i32 0, <4 x i32> %rsrc, i32 0, i32 1, i64 %exec, <4 x float> zeroinitializer) |
| |
| ret <4 x float> %result |
| } |
| |
| ;inline asm does not construct waterfall; user should be responsible |
| define amdgpu_kernel void @inlineasm_sgpr_constraint_divergent_value(ptr addrspace(1) %out, i32 %uniform) { |
| ; CHECK-LABEL: inlineasm_sgpr_constraint_divergent_value: |
| ; CHECK: ; %bb.0: ; %entry |
| ; CHECK-NEXT: s_load_dwordx2 s[0:1], s[4:5], 0x24 |
| ; CHECK-NEXT: v_and_b32_e32 v0, 0x3ff, v0 |
| ; CHECK-NEXT: s_nop 0 |
| ; CHECK-NEXT: v_readfirstlane_b32 s2, v0 |
| ; CHECK-NEXT: ;;#ASMSTART |
| ; CHECK-NEXT: s_add_u32 s2, s2, 1 |
| ; CHECK-NEXT: ;;#ASMEND |
| ; CHECK-NEXT: v_lshlrev_b32_e32 v0, 2, v0 |
| ; CHECK-NEXT: s_nop 0 |
| ; CHECK-NEXT: v_mov_b32_e32 v1, s2 |
| ; CHECK-NEXT: s_waitcnt lgkmcnt(0) |
| ; CHECK-NEXT: global_store_dword v0, v1, s[0:1] |
| ; CHECK-NEXT: s_endpgm |
| entry: |
| %tid = call i32 @llvm.amdgcn.workitem.id.x() |
| %result = call i32 asm sideeffect "s_add_u32 $0, $1, 1", "=s,s"(i32 %tid) |
| %gep = getelementptr i32, ptr addrspace(1) %out, i32 %tid |
| store i32 %result, ptr addrspace(1) %gep |
| ret void |
| } |
| |
| define amdgpu_kernel void @inlineasm_with_salu_to_valu_uses(ptr addrspace(1) %out, i32 %base) { |
| ; CHECK-LABEL: inlineasm_with_salu_to_valu_uses: |
| ; CHECK: ; %bb.0: ; %entry |
| ; CHECK-NEXT: s_load_dwordx2 s[0:1], s[4:5], 0x24 |
| ; CHECK-NEXT: v_and_b32_e32 v0, 0x3ff, v0 |
| ; CHECK-NEXT: v_add_u32_e32 v1, 10, v0 |
| ; CHECK-NEXT: v_lshlrev_b32_e32 v0, 2, v0 |
| ; CHECK-NEXT: v_readfirstlane_b32 s2, v1 |
| ; CHECK-NEXT: ;;#ASMSTART |
| ; CHECK-NEXT: s_add_u32 s2, s2, 5 |
| ; CHECK-NEXT: ;;#ASMEND |
| ; CHECK-NEXT: s_nop 1 |
| ; CHECK-NEXT: v_mad_u32_u24 v1, v1, 3, s2 |
| ; CHECK-NEXT: s_waitcnt lgkmcnt(0) |
| ; CHECK-NEXT: global_store_dword v0, v1, s[0:1] |
| ; CHECK-NEXT: s_endpgm |
| entry: |
| %tid = call i32 @llvm.amdgcn.workitem.id.x() |
| %derived = add i32 %tid, 10 |
| |
| %asm_result = call i32 asm sideeffect "s_add_u32 $0, $1, 5", "=s,s"(i32 %derived) |
| |
| %mul_result = mul i32 %derived, 3 |
| %combined = add i32 %asm_result, %mul_result |
| %gep = getelementptr i32, ptr addrspace(1) %out, i32 %tid |
| store i32 %combined, ptr addrspace(1) %gep |
| ret void |
| } |
| |
| ; Test case with divergent value used in inline asm AND feeding into |
| ; a waterfall loop |
| define amdgpu_kernel void @inlineasm_and_waterfall_same_value(ptr addrspace(1) %out, ptr addrspace(1) %descriptors) { |
| ; CHECK-LABEL: inlineasm_and_waterfall_same_value: |
| ; CHECK: ; %bb.0: ; %entry |
| ; CHECK-NEXT: s_load_dwordx4 s[8:11], s[4:5], 0x24 |
| ; CHECK-NEXT: v_and_b32_e32 v4, 0x3ff, v0 |
| ; CHECK-NEXT: v_lshlrev_b32_e32 v0, 4, v4 |
| ; CHECK-NEXT: v_readfirstlane_b32 s0, v4 |
| ; CHECK-NEXT: ;;#ASMSTART |
| ; CHECK-NEXT: s_add_u32 s12, s0, 100 |
| ; CHECK-NEXT: ;;#ASMEND |
| ; CHECK-NEXT: s_waitcnt lgkmcnt(0) |
| ; CHECK-NEXT: global_load_dwordx4 v[0:3], v0, s[10:11] |
| ; CHECK-NEXT: s_mov_b64 s[2:3], exec |
| ; CHECK-NEXT: s_waitcnt vmcnt(0) |
| ; CHECK-NEXT: .LBB3_1: ; =>This Inner Loop Header: Depth=1 |
| ; CHECK-NEXT: v_readfirstlane_b32 s4, v0 |
| ; CHECK-NEXT: v_readfirstlane_b32 s5, v1 |
| ; CHECK-NEXT: v_readfirstlane_b32 s6, v2 |
| ; CHECK-NEXT: v_readfirstlane_b32 s7, v3 |
| ; CHECK-NEXT: v_cmp_eq_u64_e32 vcc, s[4:5], v[0:1] |
| ; CHECK-NEXT: s_nop 0 |
| ; CHECK-NEXT: v_cmp_eq_u64_e64 s[0:1], s[6:7], v[2:3] |
| ; CHECK-NEXT: s_and_b64 s[0:1], vcc, s[0:1] |
| ; CHECK-NEXT: s_and_saveexec_b64 s[0:1], s[0:1] |
| ; CHECK-NEXT: buffer_load_dword v5, v4, s[4:7], 0 offen |
| ; CHECK-NEXT: ; implicit-def: $vgpr0_vgpr1_vgpr2_vgpr3 |
| ; CHECK-NEXT: s_xor_b64 exec, exec, s[0:1] |
| ; CHECK-NEXT: s_cbranch_execnz .LBB3_1 |
| ; CHECK-NEXT: ; %bb.2: |
| ; CHECK-NEXT: s_mov_b64 exec, s[2:3] |
| ; CHECK-NEXT: s_waitcnt vmcnt(0) |
| ; CHECK-NEXT: v_add_u32_e32 v0, s12, v5 |
| ; CHECK-NEXT: v_lshlrev_b32_e32 v1, 2, v4 |
| ; CHECK-NEXT: global_store_dword v1, v0, s[8:9] |
| ; CHECK-NEXT: s_endpgm |
| entry: |
| %tid = call i32 @llvm.amdgcn.workitem.id.x() |
| |
| %asm_result = call i32 asm sideeffect "s_add_u32 $0, $1, 100", "=s,s"(i32 %tid) |
| |
| %desc_ptr = getelementptr <4 x i32>, ptr addrspace(1) %descriptors, i32 %tid |
| %desc = load <4 x i32>, ptr addrspace(1) %desc_ptr |
| %buffer_result = call i32 @llvm.amdgcn.raw.buffer.load.i32(<4 x i32> %desc, i32 %tid, i32 0, i32 0) |
| |
| %combined = add i32 %asm_result, %buffer_result |
| %out_ptr = getelementptr i32, ptr addrspace(1) %out, i32 %tid |
| store i32 %combined, ptr addrspace(1) %out_ptr |
| ret void |
| } |
| |
| ; Test case with multiple divergent SGPR inputs to verify all are handled |
| define amdgpu_kernel void @multiple_divergent_sgpr_inputs(ptr addrspace(1) %out) { |
| ; CHECK-LABEL: multiple_divergent_sgpr_inputs: |
| ; CHECK: ; %bb.0: ; %entry |
| ; CHECK-NEXT: s_load_dwordx2 s[0:1], s[4:5], 0x24 |
| ; CHECK-NEXT: v_and_b32_e32 v0, 0x3ff, v0 |
| ; CHECK-NEXT: v_add_u32_e32 v1, 10, v0 |
| ; CHECK-NEXT: v_add_u32_e32 v2, 20, v0 |
| ; CHECK-NEXT: v_readfirstlane_b32 s2, v0 |
| ; CHECK-NEXT: v_readfirstlane_b32 s3, v1 |
| ; CHECK-NEXT: v_readfirstlane_b32 s4, v2 |
| ; CHECK-NEXT: ;;#ASMSTART |
| ; CHECK-NEXT: s_add_u32 s2, s2, s3 |
| ; CHECK-NEXT: s_add_u32 s2, s2, s4 |
| ; CHECK-NEXT: ;;#ASMEND |
| ; CHECK-NEXT: v_lshlrev_b32_e32 v0, 2, v0 |
| ; CHECK-NEXT: v_mov_b32_e32 v1, s2 |
| ; CHECK-NEXT: s_waitcnt lgkmcnt(0) |
| ; CHECK-NEXT: global_store_dword v0, v1, s[0:1] |
| ; CHECK-NEXT: s_endpgm |
| entry: |
| %tid = call i32 @llvm.amdgcn.workitem.id.x() |
| %val1 = add i32 %tid, 10 |
| %val2 = add i32 %tid, 20 |
| ; Three separate divergent SGPR inputs |
| %result = call i32 asm sideeffect "s_add_u32 $0, $1, $2\0As_add_u32 $0, $0, $3", "=s,s,s,s"(i32 %tid, i32 %val1, i32 %val2) |
| %gep = getelementptr i32, ptr addrspace(1) %out, i32 %tid |
| store i32 %result, ptr addrspace(1) %gep |
| ret void |
| } |
| |
| define amdgpu_kernel void @same_value_repeated_sgpr_inputs(ptr addrspace(1) %out) { |
| ; CHECK-LABEL: same_value_repeated_sgpr_inputs: |
| ; CHECK: ; %bb.0: ; %entry |
| ; CHECK-NEXT: s_load_dwordx2 s[0:1], s[4:5], 0x24 |
| ; CHECK-NEXT: v_and_b32_e32 v0, 0x3ff, v0 |
| ; CHECK-NEXT: s_nop 0 |
| ; CHECK-NEXT: v_readfirstlane_b32 s2, v0 |
| ; CHECK-NEXT: ;;#ASMSTART |
| ; CHECK-NEXT: s_add_u32 s3, s2, s2 |
| ; CHECK-NEXT: s_add_u32 s3, s3, s2 |
| ; CHECK-NEXT: ;;#ASMEND |
| ; CHECK-NEXT: v_lshlrev_b32_e32 v0, 2, v0 |
| ; CHECK-NEXT: v_mov_b32_e32 v1, s3 |
| ; CHECK-NEXT: s_waitcnt lgkmcnt(0) |
| ; CHECK-NEXT: global_store_dword v0, v1, s[0:1] |
| ; CHECK-NEXT: s_endpgm |
| entry: |
| %tid = call i32 @llvm.amdgcn.workitem.id.x() |
| ; Same value %tid used three times as SGPR input ($1, $2, $3). |
| ; Early-clobber '&' ensures $0 gets a different register from the inputs. |
| %result = call i32 asm sideeffect "s_add_u32 $0, $1, $2\0As_add_u32 $0, $0, $3", "=&s,s,s,s"(i32 %tid, i32 %tid, i32 %tid) |
| %gep = getelementptr i32, ptr addrspace(1) %out, i32 %tid |
| store i32 %result, ptr addrspace(1) %gep |
| ret void |
| } |
| |
| define amdgpu_kernel void @physreg_sgpr_constraint_divergent_value(ptr addrspace(1) %out) { |
| ; CHECK-LABEL: physreg_sgpr_constraint_divergent_value: |
| ; CHECK: ; %bb.0: ; %entry |
| ; CHECK-NEXT: s_load_dwordx2 s[2:3], s[4:5], 0x24 |
| ; CHECK-NEXT: v_and_b32_e32 v0, 0x3ff, v0 |
| ; CHECK-NEXT: s_nop 0 |
| ; CHECK-NEXT: v_readfirstlane_b32 s0, v0 |
| ; CHECK-NEXT: ;;#ASMSTART |
| ; CHECK-NEXT: s_add_u32 s0, s0, 1 |
| ; CHECK-NEXT: ;;#ASMEND |
| ; CHECK-NEXT: v_lshlrev_b32_e32 v0, 2, v0 |
| ; CHECK-NEXT: s_nop 0 |
| ; CHECK-NEXT: v_mov_b32_e32 v1, s0 |
| ; CHECK-NEXT: s_waitcnt lgkmcnt(0) |
| ; CHECK-NEXT: global_store_dword v0, v1, s[2:3] |
| ; CHECK-NEXT: s_endpgm |
| entry: |
| %tid = call i32 @llvm.amdgcn.workitem.id.x() |
| ; Use physical register constraint {s0} with divergent value |
| %result = call i32 asm sideeffect "s_add_u32 $0, $0, 1", "={s0},{s0}"(i32 %tid) |
| %gep = getelementptr i32, ptr addrspace(1) %out, i32 %tid |
| store i32 %result, ptr addrspace(1) %gep |
| ret void |
| } |
| |
| define amdgpu_kernel void @sgpr_tuple_i64_constraint(ptr addrspace(1) %out) { |
| ; CHECK-LABEL: sgpr_tuple_i64_constraint: |
| ; CHECK: ; %bb.0: ; %entry |
| ; CHECK-NEXT: s_load_dwordx2 s[0:1], s[4:5], 0x24 |
| ; CHECK-NEXT: v_and_b32_e32 v0, 0x3ff, v0 |
| ; CHECK-NEXT: s_mov_b32 s3, 0 |
| ; CHECK-NEXT: v_readfirstlane_b32 s2, v0 |
| ; CHECK-NEXT: ;;#ASMSTART |
| ; CHECK-NEXT: s_nop 0 |
| ; CHECK-NEXT: ;;#ASMEND |
| ; CHECK-NEXT: v_lshlrev_b32_e32 v0, 2, v0 |
| ; CHECK-NEXT: s_nop 0 |
| ; CHECK-NEXT: v_mov_b32_e32 v1, s2 |
| ; CHECK-NEXT: s_waitcnt lgkmcnt(0) |
| ; CHECK-NEXT: global_store_dword v0, v1, s[0:1] |
| ; CHECK-NEXT: s_endpgm |
| entry: |
| %tid = call i32 @llvm.amdgcn.workitem.id.x() |
| %tid64 = zext i32 %tid to i64 |
| %result = call i64 asm sideeffect "s_nop 0", "=s,s"(i64 %tid64) |
| %trunc = trunc i64 %result to i32 |
| %gep = getelementptr i32, ptr addrspace(1) %out, i32 %tid |
| store i32 %trunc, ptr addrspace(1) %gep |
| ret void |
| } |
| |
| define amdgpu_kernel void @sgpr_tuple_v2i32_constraint(ptr addrspace(1) %out, ptr addrspace(1) %in) { |
| ; CHECK-LABEL: sgpr_tuple_v2i32_constraint: |
| ; CHECK: ; %bb.0: ; %entry |
| ; CHECK-NEXT: s_load_dwordx4 s[0:3], s[4:5], 0x24 |
| ; CHECK-NEXT: v_and_b32_e32 v2, 0x3ff, v0 |
| ; CHECK-NEXT: v_lshlrev_b32_e32 v0, 3, v2 |
| ; CHECK-NEXT: s_waitcnt lgkmcnt(0) |
| ; CHECK-NEXT: global_load_dwordx2 v[0:1], v0, s[2:3] |
| ; CHECK-NEXT: s_waitcnt vmcnt(0) |
| ; CHECK-NEXT: v_readfirstlane_b32 s3, v1 |
| ; CHECK-NEXT: v_readfirstlane_b32 s2, v0 |
| ; CHECK-NEXT: ;;#ASMSTART |
| ; CHECK-NEXT: s_nop 0 |
| ; CHECK-NEXT: ;;#ASMEND |
| ; CHECK-NEXT: v_lshlrev_b32_e32 v0, 2, v2 |
| ; CHECK-NEXT: s_nop 0 |
| ; CHECK-NEXT: v_mov_b32_e32 v1, s2 |
| ; CHECK-NEXT: global_store_dword v0, v1, s[0:1] |
| ; CHECK-NEXT: s_endpgm |
| entry: |
| %tid = call i32 @llvm.amdgcn.workitem.id.x() |
| %gep_in = getelementptr <2 x i32>, ptr addrspace(1) %in, i32 %tid |
| %val = load <2 x i32>, ptr addrspace(1) %gep_in |
| %result = call <2 x i32> asm sideeffect "s_nop 0", "=s,s"(<2 x i32> %val) |
| %elem = extractelement <2 x i32> %result, i32 0 |
| %gep = getelementptr i32, ptr addrspace(1) %out, i32 %tid |
| store i32 %elem, ptr addrspace(1) %gep |
| ret void |
| } |