[AMDGPU][UniformityAnalysis] Single-input (LCSSA) phi forwarding a lane mask out of a divergent-exit cycle is marked temporally divergent
- Dominant language
- LLVM
- Stars
- 40.5k
- Forks
- 18.7k
- PR merge metrics
- PR metrics pending
Description
A single-input PHI (e.g. an LCSSA phi) that merely forwards an always-uniform lane mask (llvm.amdgcn.if.break, SI_IF/SI_ELSE results) out of a divergent-exit cycle is reported as temporally divergent by uniformity analysis. The lane-mask def itself stays uniform (it's AlwaysUniform), but the trivial phi wrapping it does not, so on the GlobalISel -new-reg-bank-select path the phi is assigned a VGPR and the subsequent llvm.amdgcn.end.cf use fails reg-bank-legalize.
This becomes reachable once LCSSA runs on the GlobalISel path (the result is the same whether the phi comes from LCSSA or is written directly).
**Reproducer:**
```llvm
; opt -mtriple=amdgcn-amd-amdpal -passes='print' -disable-output %s
define amdgpu_cs void @loop_with_1break() {
entry:
%tid = call i32 @llvm.amdgcn.workitem.id.x()
br label %A
A:
%phi.broken = phi i32 [ %if.break, %Flow ], [ 0, %entry ]
%counter = phi i32 [ 0, %entry ], [ %counter.next, %Flow ]
%a.cond = icmp ne i32 %tid, %counter
%if = call { i1, i32 } @llvm.amdgcn.if.i32(i1 %a.cond)
%if.bool = extractvalue { i1, i32 } %if, 0
%if.mask = extractvalue { i1, i32 } %if, 1
br i1 %if.bool, label %loop.body, label %Flow
loop.body:
%counter.plus.1 = add i32 %counter, 1
%x.cond = icmp ult i32 %counter, 100
br label %Flow
Flow:
%counter.next = phi i32 [ %counter.plus.1, %loop.body ], [ poison, %A ]
%loop.cond = phi i1 [ %x.cond, %loop.body ], [ true, %A ]
call void @llvm.amdgcn.end.cf.i32(i32 %if.mask)
%if.break = call i32 @llvm.amdgcn.if.break.i32(i1 %loop.cond, i32 %phi.broken)
%loop = call i1 @llvm.amdgcn.loop.i32(i32 %if.break)
br i1 %loop, label %exit, label %A
exit:
%.lcssa = phi i32 [ %if.break, %Flow ]
call void @llvm.amdgcn.end.cf.i32(i32 %.lcssa)
ret void
}
```
**Analysis Output:**
```
TEMPORAL DIVERGENCE LIST:
Value : %if.break = call i32 @llvm.amdgcn.if.break.i32(i1 %loop.cond, i32 %phi.broken)
Used by : %.lcssa = phi i32 [ %if.break, %Flow ]
...
BLOCK Flow: %if.break = call ... ; uniform (AlwaysUniform)
BLOCK exit: DIVERGENT: %.lcssa = phi ... ; the forwarding phi is divergent
```
**Root cause:**
In GenericUniformityAnalysisImpl::analyzeCycleExitDivergence, the exit-block-phi pass marks every exit phi that reads a cycle-defined value as divergent, markDivergent early-returns for isAlwaysUniform values, which protects the lane-mask def (if.break) but not the trivial phi forwarding it. propagateTemporalDivergence then also records the (%if.break → %.lcssa) pair in the temporal-divergence list.
**Design question (for discussion)**
What is the cleanest way to encode "temporally uniform" so the generic analysis can use it?
- I have thought of target hook (e.g. extend TargetInstrInfo::getValueUniformity / a new isLaneMask): smallest, but adds another target-specific knob in target-independent code.
Contributor guide
Assessment
This issue has not been assessed yet.