[llvm] [Transforms/Util] Add SimplifySwitchVar pass (PR #149937)

Nikita Popov via llvm-commits llvm-commits at lists.llvm.org
Fri Jul 25 13:49:57 PDT 2025


nikic wrote:

> > At a high level, I don't think that this is the correct approach to the problem. We should be sinking those GEPs such that there is one GEP with a phi operand, and then existing switch simplification will take care of the rest.
> 
> So I think we missed that a similar switch simplification already exists because there are fairly straightforward cases that are currently not simplified which are relevant for what we're targeting here.
> 
> It may still be the case that the "correct" answer is:
> 
>     * Generalize the existing simplification so that it can handle these cases
> 
>     * Strengthen the sinking of `getelementptr`s
> 
> 
> I'm a little worried however that the relative tradeoff of when these transforms make sense is quite different across architectures. Do you have any thoughts about that?
> 
> Here's a case that currently isn't simplified:
> 
> ```
> define i32 @test(i32 %x) {
> entry:
>   switch i32 %x, label %end [
>     i32 0, label %case0
>     i32 1, label %case1
>     i32 2, label %case2
>     i32 3, label %case3
>   ]
> 
> case0:
>   br label %end
> case1:
>   br label %end
> case2:
>   br label %end
> case3:
>   br label %end
> 
> end:
>   %idx = phi i32 [ 0, %case0 ], [ 3, %case1 ], [ 6, %case2 ], [ 9, %case3 ], [ 12, %entry ]
>   ret i32 %idx
> }
> ```
> 
> This one is pretty straightforward.

This case is already simplified -- this is one of the most basic cases. The relevant code is behind `fitsInLegalInteger`, so you'll have to specify a triple or `n` data layout to get it to actually work.

> The following is less straightforward:
> 
> ```
> declare void @foo1()
> declare void @foo2()
> declare void @foo3()
> declare void @foo4()
> 
> define i32 @test(i32 %x) {
> entry:
>   switch i32 %x, label %end [
>     i32 0, label %case0
>     i32 1, label %case1
>     i32 2, label %case2
>     i32 3, label %case3
>   ]
> 
> case0:
>   call void @foo1()
>   br label %end
> case1:
>   call void @foo2()
>   br label %end
> case2:
>   call void @foo3()
>   br label %end
> case3:
>   call void @foo4()
>   br label %end
> 
> end:
>   %idx = phi i32 [ 0, %case0 ], [ 1, %case1 ], [ 2, %case2 ], [ 3, %case3 ], [ 4, %entry ]
>   ret i32 %idx
> }
> ```
> 
> (The calls stand in for arbitrary code.)
> 
> In order to handle this case, we have to extend the lifetime of `%idx` or `%x`. This is very typically worth it in the case we're targeting (on AMDGPU, which has lots of registers), but may be less of a slam dunk on other architectures.
> 
> Going at it from the other end, do you think there's a reason why the existing instruction sinking prefers not to sink those longer sequences of instructions? That was really my main concern going into this.
> 
> It's generally quite difficult to do any of this in a way that makes all targets happy. That's really the biggest reason why this ended up as a separate pass.

I think we'd transform that case without the entry edge.

https://github.com/llvm/llvm-project/pull/149937


More information about the llvm-commits mailing list