Shaders that use the llvm.amdgcn.init.whole.wave intrinsic need to
explicitly preserve the inactive lanes of VGPRs of interest by adding
them as dummy arguments. The code usually looks something like this:
```
define amdgcn_cs_chain void f(active vgpr args..., i32 %inactive.vgpr1, ..., i32 %inactive.vgprN) {
entry:
%c = call i1 @llvm.amdgcn.init.whole.wave()
br i1 %c, label %shader, label %tail
shader:
[...]
tail:
%inactive.vgpr.arg1 = phi i32 [ %inactive.vgpr1, %entry], [poison, %shader]
[...]
; %inactive.vgpr* then get passed into a llvm.amdgcn.cs.chain call
```
Unfortunately, this kind of phi node will get optimized away and the
backend won't be able to figure out that it's ok to use the active lanes
of `%inactive.vgpr*` inside `shader`.
This patch fixes the issue by introducing a llvm.amdgcn.dead intrinsic,
whose result can be used as a PHI operand instead of the poison. This
will be selected to an IMPLICIT_DEF, which the backend can work with.
At the moment, the llvm.amdgcn.dead intrinsic works only on i32 values.
Support for other types can be added later if needed.
65 lines
2.6 KiB
LLVM
65 lines
2.6 KiB
LLVM
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5
|
|
; RUN: llc -global-isel=0 -mtriple=amdgcn -mcpu=gfx1200 < %s | FileCheck -check-prefix=ASM-DAG %s
|
|
; RUN: llc -global-isel=1 -mtriple=amdgcn -mcpu=gfx1200 < %s | FileCheck -check-prefix=ASM-GISEL %s
|
|
|
|
; Test that we can use v0 for temporaries in the if.then block.
|
|
define i32 @dead(i1 %cond, i32 %x, ptr addrspace(1) %ptr1, ptr addrspace(1) %ptr2) #0 {
|
|
; ASM-DAG-LABEL: dead:
|
|
; ASM-DAG: ; %bb.0: ; %entry
|
|
; ASM-DAG-NEXT: s_wait_loadcnt_dscnt 0x0
|
|
; ASM-DAG-NEXT: s_wait_expcnt 0x0
|
|
; ASM-DAG-NEXT: s_wait_samplecnt 0x0
|
|
; ASM-DAG-NEXT: s_wait_bvhcnt 0x0
|
|
; ASM-DAG-NEXT: s_wait_kmcnt 0x0
|
|
; ASM-DAG-NEXT: v_mov_b32_e32 v4, v0
|
|
; ASM-DAG-NEXT: v_mov_b32_e32 v0, v1
|
|
; ASM-DAG-NEXT: s_mov_b32 s0, exec_lo
|
|
; ASM-DAG-NEXT: s_delay_alu instid0(VALU_DEP_2) | instskip(NEXT) | instid1(VALU_DEP_1)
|
|
; ASM-DAG-NEXT: v_and_b32_e32 v1, 1, v4
|
|
; ASM-DAG-NEXT: v_cmpx_eq_u32_e32 1, v1
|
|
; ASM-DAG-NEXT: s_cbranch_execz .LBB0_2
|
|
; ASM-DAG-NEXT: ; %bb.1: ; %if.then
|
|
; ASM-DAG-NEXT: v_add_nc_u32_e32 v0, 1, v0
|
|
; ASM-DAG-NEXT: global_store_b32 v[2:3], v0, off
|
|
; ASM-DAG-NEXT: ; implicit-def: $vgpr0
|
|
; ASM-DAG-NEXT: .LBB0_2: ; %if.end
|
|
; ASM-DAG-NEXT: s_wait_alu 0xfffe
|
|
; ASM-DAG-NEXT: s_or_b32 exec_lo, exec_lo, s0
|
|
; ASM-DAG-NEXT: s_setpc_b64 s[30:31]
|
|
;
|
|
; ASM-GISEL-LABEL: dead:
|
|
; ASM-GISEL: ; %bb.0: ; %entry
|
|
; ASM-GISEL-NEXT: s_wait_loadcnt_dscnt 0x0
|
|
; ASM-GISEL-NEXT: s_wait_expcnt 0x0
|
|
; ASM-GISEL-NEXT: s_wait_samplecnt 0x0
|
|
; ASM-GISEL-NEXT: s_wait_bvhcnt 0x0
|
|
; ASM-GISEL-NEXT: s_wait_kmcnt 0x0
|
|
; ASM-GISEL-NEXT: v_mov_b32_e32 v4, v0
|
|
; ASM-GISEL-NEXT: v_mov_b32_e32 v0, v1
|
|
; ASM-GISEL-NEXT: s_mov_b32 s0, exec_lo
|
|
; ASM-GISEL-NEXT: s_delay_alu instid0(VALU_DEP_2) | instskip(NEXT) | instid1(VALU_DEP_1)
|
|
; ASM-GISEL-NEXT: v_and_b32_e32 v1, 1, v4
|
|
; ASM-GISEL-NEXT: v_cmpx_ne_u32_e32 0, v1
|
|
; ASM-GISEL-NEXT: s_cbranch_execz .LBB0_2
|
|
; ASM-GISEL-NEXT: ; %bb.1: ; %if.then
|
|
; ASM-GISEL-NEXT: v_add_nc_u32_e32 v0, 1, v0
|
|
; ASM-GISEL-NEXT: global_store_b32 v[2:3], v0, off
|
|
; ASM-GISEL-NEXT: ; implicit-def: $vgpr0
|
|
; ASM-GISEL-NEXT: .LBB0_2: ; %if.end
|
|
; ASM-GISEL-NEXT: s_wait_alu 0xfffe
|
|
; ASM-GISEL-NEXT: s_or_b32 exec_lo, exec_lo, s0
|
|
; ASM-GISEL-NEXT: s_setpc_b64 s[30:31]
|
|
entry:
|
|
%dead = call i32 @llvm.amdgcn.dead.i32()
|
|
br i1 %cond, label %if.then, label %if.end
|
|
|
|
if.then: ; preds = %entry
|
|
%temp = add i32 %x, 1
|
|
store i32 %temp, ptr addrspace(1) %ptr1
|
|
br label %if.end
|
|
|
|
if.end:
|
|
%res = phi i32 [ %x, %entry ], [ %dead, %if.then ]
|
|
ret i32 %res
|
|
}
|