-
Notifications
You must be signed in to change notification settings - Fork 13k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
shuffle undef mask on vectors with poison elements #43303
Comments
Unless I'm mis-remembering the history and based on the discussion in: The example and LangRef are correct as-is. We decided that an undef element of a shuffle mask means the result really is undefined and does not need to be picked from one of the input vectors. I'm not sure if the definition of poison has been refined since that time to alter that decision. The current/existing IR transform makes the most sense for optimization. Ie, if we decide that the example needs to be changed to something more specific, then we may lose optimizations (probably not for this simple example, but something involving larger IR-to-IR transforms): define <4 x float> @less_poison(float %arg) { (The shuffle mask no longer contains an undef element, so that's not a simple "splat".) |
Do we allow elements of a vector to be independently poisoned? LangRef currently says we don't, but I'm not sure that's actually what we implement. If we do allow elements to be independently poisoned, shufflevector needs to explicitly specify the interaction with poison, I think. Consider the following example: define <4 x float> @shuffle(<4 x float> %arg) { instcombine currently combines away the shuffle. But if lane 0 is poison, that's propagating poison in a case where it wouldn't otherwise. |
I believe a lot of nice vector combines would go away if poison was per-vector instead of per-element. This is something we could demonstrate using alive2 pretty easily, if anyone needs convincing. |
Having some mask element = undef is equivalent to a mask that is OOB (since we can make the undef value take a large value). In Alive we implemented this OOB case as poison, since we really need to get rid of undef ASAP (to make GVN correct, for example). My additional worry with generating undef is that it's very easy to fall in the trap of "pick one of undef, %foo -> %foo", which is incorrect if %foo is poison. For example: instcombine transforms this to: This is incorrect in case %arg[1] is poison. It seems the optimization problems would go away if we were to initialize vector with poison values rather than undef values, like: This would be correct. We can change the semantics in Alive for the OOB case and make that undef for now, and revisit later when we introduce the poison value and commit to removing undef. |
This transform happens in 2 steps internally inside instcombine. The 1st step changes the shuffle mask element that is choosing the undef vector operand: %shuf = shufflevector <2 x float> %arg, <2 x float> undef, <2 x i32> <i32 0, i32 2> --> %shuf = shufflevector <2 x float> %arg, <2 x float> undef, <2 x i32> <i32 0, i32 undef> The reasoning is: SelectionDAG also has that kind of transform, so removing that in IR may not affect the end result that much. But we have poison in SDAG too, so we wouldn't completely fix this by changing instcombine alone. But IIUC, we're saying we can sweep this under the rug for now and wait for a poison value to be introduced as another (final?) step to removing undef. Once the shuffle mask has an undef element, we're back to the same example Eli noted in comment 2. |
Ah, looking again at Eli's example: define <4 x float> @shuffle(<4 x float> %arg) { I agree that we shouldn't be doing this because it transforms undef into poison. For the example above, here's an end-to-end miscompilation: %val = extractelement <2 x i32> %v2, i32 0 If %v2 was <undef, %x_1> as it should, then %cmp couldn't be true, since "undef > %x" isn't always true. |
Given a chance to punt on making intermediate/temporary changes, I'll take it. :) |
Fair enough :) I would ignore the other transformation we reported in this bug report. That one seems safe; we'll run more tests and report back if anything pops up. |
I think I cc'd everyone's corresponding Phab handle, but just in case: |
Code fix: Doc fix proposal: |
Anything left to do here? We clarified the reasoning about shuffle masks and documented it slightly better with: |
Ok, I agree we can close this one. |
mentioned in issue llvm/llvm-bugzilla-archive#44185 |
Extended Description
A shuffle undef mask should select either of the elements. If one of them happens to be poison, then the result can be poison. The langref [1] and a transformation in instcombine seems forgot considering the poison case.
See below the transformation of insertelement to shufflevector. Assume the function input %arg is poison, the target program shuffles the vector <poison, undef, undef, undef> with mask <undef, 0, 0, 0>, the result should be <poison, poison, poison, poison>, whereas the source program evaluates to <undef, poison, poison, poison>. The target is more poisonous than the source.
llvm/test/Transforms/InstCombine/broadcast.ll
define <4 x float> @splat_undef1(float %arg) {
; CHECK-LABEL: @splat_undef1(
; CHECK-NEXT: [[TMP1:%.]] = insertelement <4 x float> undef, float [[ARG:%.]], i32 0
; CHECK-NEXT: [[T6:%.*]] = shufflevector <4 x float> [[TMP1]], <4 x float> undef, <4 x i32> <i32 undef, i32 0, i32 0, i32 0>
; CHECK-NEXT: ret <4 x float> [[T6]]
;
%t = insertelement <4 x float> undef, float %arg, i32 1
%t4 = insertelement <4 x float> %t, float %arg, i32 1
%t5 = insertelement <4 x float> %t4, float %arg, i32 2
%t6 = insertelement <4 x float> %t5, float %arg, i32 3
ret <4 x float> %t6
}
[1] https://llvm.org/docs/LangRef.html#id186
The text was updated successfully, but these errors were encountered: