Float undef value propagation

Constant propagation pass generates constant expression when undef is used in float instructions instead of propagating the undef value.

; Function Attrs: nounwind

define float @_Z1fv() #0 {

entry:

%add = fadd fast float undef, 2.000000e+00

ret float %add

}

Becomes:

; Function Attrs: nounwind

define float @_Z1fv() #0 {

entry:

ret float fadd (float undef, float 2.000000e+00)

}

Is it safe to transform “%add = fadd fast float undef, 2.000000e+00” to “undef”? Is there a reason why constant propagation pass doesn’t do this transformation?

Thanks,

Thomas

I’m not totally sure this is safe. Not saying it isn’t, but the wording around undef and bit patterns in the language spec is concerning me. Could there be a case where some bit of the result is known given only one constant argument? I’m not familiar enough with the floating point semantics of LLVM’s IR, but suspect there might be such a case. A few cases worth thinking about: undef + max_float, undef + NAN (signaling or non-signalling). If someone more familiar with floating point knows better, please feel free to chime in. On the other hand, the following transformation is definitely safe: } (undef could be zero, thus result of the add is the second argument) If we don’t apply that transform, we probably should. (Well, assuming there’s not some normalization/rounding trap here. I don’t think there is, but I’m not totally sure.) Yours, Philip

From: "Philip Reames" <listmail@philipreames.com>
To: llvmdev@cs.uiuc.edu
Sent: Tuesday, December 10, 2013 2:55:36 PM
Subject: Re: [LLVMdev] Float undef value propagation

Constant propagation pass generates constant expression when undef is
used in float instructions instead of propagating the undef value.

; Function Attrs: nounwind

define float @_Z1fv() #0 {

entry:

%add = fadd fast float undef, 2.000000e+00

ret float %add

}

Becomes:

; Function Attrs: nounwind

define float @_Z1fv() #0 {

entry:

ret float fadd (float undef, float 2.000000e+00)

}

Is it safe to transform “%add = fadd fast float undef, 2.000000e+00”
to “undef”? Is there a reason why constant propagation pass doesn’t
do this transformation? I'm not totally sure this is safe. Not
saying it isn't, but the wording around undef and bit patterns in
the language spec is concerning me. Could there be a case where some
bit of the result is known given only one constant argument? I'm not
familiar enough with the floating point semantics of LLVM's IR, but
suspect there might be such a case. A few cases worth thinking
about: undef + max_float, undef + NAN (signaling or non-signalling).
If someone more familiar with floating point knows better, please
feel free to chime in.

Out of curosity, do we currently perform this transformation when -enable-unsafe-fp-math is specified (-ffast-math from Clang)?

-Hal

You are right some cases would definitely not be right like undef + Nan → undef. For 2.0f case I’m not sure either if any bits could be known.

It seems that in general fadd( float undef, float %1) → float %1 should always be safe and I just checked with latest code this doesn’t happen.

Do you think the right solution would be to add such optimization? Is there any reason why we keep some arithmetic constant expressions while those could always be evaluated as far as I understand (unless it uses global pointers)?

In this example there might be cases where we would be able to propagate undef which could generate better code.

Thanks,

Thomas

Do you have an example where that's unsafe? You usually have to do
something pretty bad to get an undef in the first place. Since
IEEE-754 doesn't have that concept I can't see how it could have
anything to say on how LLVM optimises it.

Cheers.

Tim.

Out of curosity, do we currently perform this transformation when -enable-unsafe-fp-math is specified (-ffast-math from Clang)?
-Hal

Actually the example I sent was compiled with unsafe math and it doesn't happen
Thomas

Well in IEEE-754 Nan + any value returns Nan, so doing Nan + undef -> undef is wrong.

I agree that undef are usually the results of a bad application behavior I'm just wondering what is the right thing to do.

Thomas

I see what you mean having re-read the language reference. It looks
like LLVM's "undef" has to be *some* bitpattern at any point, and that
allows IEEE room to come in and say what the result is.

Tim.

To generalize this, undef can be any bit pattern, but it still has to respect universal constraints of its type. This means that predicates that are true for all values of a type must also be true for undef. Because NaN + x == NaN is true for all values of x, it must also be true for undef.

—Owen

Seems like this might be another pattern to consider adding (if we don’t already have it.) Just to review the patterns which we’ve discussed so far: NaN + any == NaN undef + any == NaN (since undef can be NaN) or undef + any (since undef could be zero) Philip

undef + non-NaN is still undef. The compiler is free to choose any value of the type it wishes when simplifying an undef expression. The important point is that it still has to be a value of that type. Hence, predicates that are true for any choice of value must still be respected. This is the case for NaN + undef == NaN: while the compiler is free to choose any value for the undef, there is no such value for which the result is not NaN.

—Owen

undef + non-NaN is not undef. This is because while you can pick any value
for the original undef, if you leave an undef behind, you can't control
what someone else might pick for it. There are floating-point values which
cannot be the result of undef + non-NaN for many values of non-NaN, so the
result of undef + non-Nan is not an unconstrained undef.

Dan

This is a very good point. Does this mean that the only valid transformations of "undef + Constant" are "Constant" (by undef == 0) or "NaN" (by undef == NaN)? Or are there known easily describible subsets of Constants which allow a more general transform?

As an example, when Constant == 0, it would appear that propagating the undef is fine. (I think.) Are there other cases like this? Or am I wrong about the legality of this example?

Philip

For every non-NaN, non-Inf constant C, C + (-C) = 0, right?
So I guess you could also take undef = -C, and get 0.

I'm not sure whether you can do the same for multiplication (taking undef = 1/C and getting 1), though.

Hi!

-0.0 is never the result of adding 0.0 to something, so even that isn't
safe (outside of -ffast-math mode)

Dan

Dan, thank you for the clarification. Your responses have been very helpful in understanding corner cases. I think that we’ve identified a couple of transformations which would be worthwhile adding to SimplifyFAddInst. Would you agree? Here are the cases I’m considering: NaN + any == NaN undef + any == NaN (since undef can be NaN, can not legally propagate RHS) I believe based on our discussion that both of these are correct, even outside of fast-math. Do you agree? I’m happy to prepare a patch, but want to get agreement on the legality of the transformations first. Just to check my understand, the following is NOT legal: undef + undef = undef (since the output could be -0.0 which is not a legal outcome for an fadd) Yours, Philip