| ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6 | |
| ; RUN: llc -mtriple=amdgpu9.00 < %s | FileCheck -check-prefix=GFX9 %s | |
| ; RUN: llc -mtriple=amdgpu10.10 < %s | FileCheck -check-prefix=GFX10 %s | |
| ; Verify that uniform (SGPR) byte-packing operations produce v_perm_b32 | |
| ; instead of scalar s_and/s_lshl/s_or sequences. The OR and AND DAG combines | |
| ; that create PERM nodes were previously gated on isDivergent(), preventing | |
| ; uniform values from using the more efficient v_perm_b32 instruction. | |
| ; OR combine: pack low bytes of two uniform values. | |
| define amdgpu_kernel void @or_uniform_two_bytes(ptr addrspace(1) %out, i32 inreg %a, i32 inreg %b) { | |
| ; GFX9-LABEL: or_uniform_two_bytes: | |
| ; GFX9: ; %bb.0: | |
| ; GFX9-NEXT: s_load_dwordx4 s[0:3], s[4:5], 0x24 | |
| ; GFX9-NEXT: v_mov_b32_e32 v2, 0x6050400 | |
| ; GFX9-NEXT: v_mov_b32_e32 v0, 0 | |
| ; GFX9-NEXT: s_waitcnt lgkmcnt(0) | |
| ; GFX9-NEXT: s_and_b32 s3, s3, 0xff | |
| ; GFX9-NEXT: v_mov_b32_e32 v1, s2 | |
| ; GFX9-NEXT: v_perm_b32 v1, s3, v1, v2 | |
| ; GFX9-NEXT: global_store_dword v0, v1, s[0:1] | |
| ; GFX9-NEXT: s_endpgm | |
| ; | |
| ; GFX10-LABEL: or_uniform_two_bytes: | |
| ; GFX10: ; %bb.0: | |
| ; GFX10-NEXT: s_load_dwordx4 s[0:3], s[4:5], 0x24 | |
| ; GFX10-NEXT: v_mov_b32_e32 v0, 0x6050400 | |
| ; GFX10-NEXT: v_mov_b32_e32 v1, 0 | |
| ; GFX10-NEXT: s_waitcnt lgkmcnt(0) | |
| ; GFX10-NEXT: s_and_b32 s3, s3, 0xff | |
| ; GFX10-NEXT: v_perm_b32 v0, s3, s2, v0 | |
| ; GFX10-NEXT: global_store_dword v1, v0, s[0:1] | |
| ; GFX10-NEXT: s_endpgm | |
| %a.byte = and i32 %a, 255 | |
| %b.byte = and i32 %b, 255 | |
| %b.shifted = shl i32 %b.byte, 8 | |
| %packed = or i32 %a.byte, %b.shifted | |
| store i32 %packed, ptr addrspace(1) %out | |
| ret void | |
| } | |
| ; OR combine: pack 4 uniform i8 values (the WMMA scale operand pattern). | |
| define amdgpu_kernel void @or_uniform_v4i8(ptr addrspace(1) %out, i8 inreg %a, i8 inreg %b) { | |
| ; GFX9-LABEL: or_uniform_v4i8: | |
| ; GFX9: ; %bb.0: | |
| ; GFX9-NEXT: s_load_dword s2, s[4:5], 0x2c | |
| ; GFX9-NEXT: s_load_dwordx2 s[0:1], s[4:5], 0x24 | |
| ; GFX9-NEXT: v_mov_b32_e32 v2, 0x1010104 | |
| ; GFX9-NEXT: v_mov_b32_e32 v0, 0 | |
| ; GFX9-NEXT: s_waitcnt lgkmcnt(0) | |
| ; GFX9-NEXT: s_and_b32 s3, s2, 0xff | |
| ; GFX9-NEXT: v_mov_b32_e32 v1, s2 | |
| ; GFX9-NEXT: v_perm_b32 v1, s3, v1, v2 | |
| ; GFX9-NEXT: global_store_dword v0, v1, s[0:1] | |
| ; GFX9-NEXT: s_endpgm | |
| ; | |
| ; GFX10-LABEL: or_uniform_v4i8: | |
| ; GFX10: ; %bb.0: | |
| ; GFX10-NEXT: s_clause 0x1 | |
| ; GFX10-NEXT: s_load_dword s2, s[4:5], 0x2c | |
| ; GFX10-NEXT: s_load_dwordx2 s[0:1], s[4:5], 0x24 | |
| ; GFX10-NEXT: v_mov_b32_e32 v0, 0x1010104 | |
| ; GFX10-NEXT: v_mov_b32_e32 v1, 0 | |
| ; GFX10-NEXT: s_waitcnt lgkmcnt(0) | |
| ; GFX10-NEXT: s_and_b32 s3, s2, 0xff | |
| ; GFX10-NEXT: v_perm_b32 v0, s3, s2, v0 | |
| ; GFX10-NEXT: global_store_dword v1, v0, s[0:1] | |
| ; GFX10-NEXT: s_endpgm | |
| %v0 = insertelement <4 x i8> poison, i8 %a, i64 0 | |
| %v1 = insertelement <4 x i8> %v0, i8 %b, i64 1 | |
| %v2 = insertelement <4 x i8> %v1, i8 %b, i64 2 | |
| %v3 = insertelement <4 x i8> %v2, i8 %b, i64 3 | |
| %packed = bitcast <4 x i8> %v3 to i32 | |
| store i32 %packed, ptr addrspace(1) %out | |
| ret void | |
| } | |
| ; OR combine: splat a uniform i8 into all 4 byte lanes. | |
| define amdgpu_kernel void @or_uniform_splat_i8(ptr addrspace(1) %out, i8 inreg %a) { | |
| ; GFX9-LABEL: or_uniform_splat_i8: | |
| ; GFX9: ; %bb.0: | |
| ; GFX9-NEXT: s_load_dword s2, s[4:5], 0x2c | |
| ; GFX9-NEXT: s_load_dwordx2 s[0:1], s[4:5], 0x24 | |
| ; GFX9-NEXT: v_mov_b32_e32 v1, 0x4040404 | |
| ; GFX9-NEXT: v_mov_b32_e32 v0, 0 | |
| ; GFX9-NEXT: s_waitcnt lgkmcnt(0) | |
| ; GFX9-NEXT: s_and_b32 s2, s2, 0xff | |
| ; GFX9-NEXT: v_perm_b32 v1, s2, s2, v1 | |
| ; GFX9-NEXT: global_store_dword v0, v1, s[0:1] | |
| ; GFX9-NEXT: s_endpgm | |
| ; | |
| ; GFX10-LABEL: or_uniform_splat_i8: | |
| ; GFX10: ; %bb.0: | |
| ; GFX10-NEXT: s_clause 0x1 | |
| ; GFX10-NEXT: s_load_dword s2, s[4:5], 0x2c | |
| ; GFX10-NEXT: s_load_dwordx2 s[0:1], s[4:5], 0x24 | |
| ; GFX10-NEXT: v_mov_b32_e32 v0, 0 | |
| ; GFX10-NEXT: s_waitcnt lgkmcnt(0) | |
| ; GFX10-NEXT: s_and_b32 s2, s2, 0xff | |
| ; GFX10-NEXT: v_perm_b32 v1, s2, s2, 0x4040404 | |
| ; GFX10-NEXT: global_store_dword v0, v1, s[0:1] | |
| ; GFX10-NEXT: s_endpgm | |
| %v0 = insertelement <4 x i8> poison, i8 %a, i64 0 | |
| %v1 = insertelement <4 x i8> %v0, i8 %a, i64 1 | |
| %v2 = insertelement <4 x i8> %v1, i8 %a, i64 2 | |
| %v3 = insertelement <4 x i8> %v2, i8 %a, i64 3 | |
| %packed = bitcast <4 x i8> %v3 to i32 | |
| store i32 %packed, ptr addrspace(1) %out | |
| ret void | |
| } | |
| ; AND combine: interleave bytes from two uniform sources using complementary | |
| ; OR masks. and(or(a, 0xFF00FF00), or(b, 0x00FF00FF)) selects even bytes from | |
| ; b and odd bytes from a. | |
| define amdgpu_kernel void @and_uniform_interleave_bytes(ptr addrspace(1) %out, i32 inreg %a, i32 inreg %b) { | |
| ; GFX9-LABEL: and_uniform_interleave_bytes: | |
| ; GFX9: ; %bb.0: | |
| ; GFX9-NEXT: s_load_dwordx4 s[0:3], s[4:5], 0x24 | |
| ; GFX9-NEXT: v_mov_b32_e32 v2, 0x7020500 | |
| ; GFX9-NEXT: v_mov_b32_e32 v0, 0 | |
| ; GFX9-NEXT: s_waitcnt lgkmcnt(0) | |
| ; GFX9-NEXT: v_mov_b32_e32 v1, s2 | |
| ; GFX9-NEXT: v_perm_b32 v1, s3, v1, v2 | |
| ; GFX9-NEXT: global_store_dword v0, v1, s[0:1] | |
| ; GFX9-NEXT: s_endpgm | |
| ; | |
| ; GFX10-LABEL: and_uniform_interleave_bytes: | |
| ; GFX10: ; %bb.0: | |
| ; GFX10-NEXT: s_load_dwordx4 s[0:3], s[4:5], 0x24 | |
| ; GFX10-NEXT: v_mov_b32_e32 v0, 0x7020500 | |
| ; GFX10-NEXT: v_mov_b32_e32 v1, 0 | |
| ; GFX10-NEXT: s_waitcnt lgkmcnt(0) | |
| ; GFX10-NEXT: v_perm_b32 v0, s3, s2, v0 | |
| ; GFX10-NEXT: global_store_dword v1, v0, s[0:1] | |
| ; GFX10-NEXT: s_endpgm | |
| %a.or = or i32 %a, -16711936 | |
| %b.or = or i32 %b, 16711935 | |
| %result = and i32 %a.or, %b.or | |
| store i32 %result, ptr addrspace(1) %out | |
| ret void | |
| } |