llvm / llvm/llvm-project

[AMDGPU][UniformityAnalysis] Single-input (LCSSA) phi forwarding a lane mask out of a divergent-exit cycle is marked temporally divergent

Open
#206,435 4 comments 0 reactions 4 assignees Claimed by @jayfoad View on GitHub
backend:AMDGPU
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

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.