[llvm-dev] [FPEnv] undef and constrained intrinsics?

Serge Pavlov via llvm-dev
Thu, 22 Jul 2021 10:34:29 -0000

The concept of undefined value has always been obscure and caused many
questions. I'd like to share my opinion, however I am not sure if I
understand this concept correctly.

LLVM documentation (https://llvm.org/docs/LangRef.html#undefined-values)
describes undefined values:
"Undefined values are useful because they indicate to the compiler that the
program is well defined no matter what value is used". So these are values
on which the result of program execution does not depend. This is why an
undefined value may be replaced by an arbitrary value of proper type and
range. The choice of the replacement value is dictated mainly by
convenience. If however the produced result depends on this choice, it
means the value of `undef` affects results, so the initial supposition is
broken and we have undefined behavior.

I agree with Sanjay that constrained intrinsics should behave in the same
way as regular FP operations with respect to `undef`. Control modes (like
rounding mode) influence result value, but we know that particular value of
`undef` is not important. FP exceptions are a bit more complex. If the
value of `undef` may be arbitrary, it is not possible to guarantee that FP
exceptions would be the same for all possible values. So we can assume that
`undef` operands do not affect FP exceptions. Either such operation is
eliminated, because its value is not used, or the operation itself does not
use the `undef` argument.

If any of standard IR FP operations has undef argument, the result may be
either `undef` or any FP value. It is convenient to use NaN in such cases.
It does not make the program more correct but it can help to detect
undefined behavior in some FP environments. However `undef` result seems
better choice than NaN, because in this case the user of `undef` value may
choose a convenient representation for `undef`.

I do not see any reason to distinguish between the cases "all operands are
undefs" and "only one operand is undef". In both cases we get a
value that
is not used in the correct program.

So I would propose transformations:

%r = call float @llvm.experimental.constrained.fadd.f32(float undef, float
undef, metadata !"round.dynamic", metadata
!"fpexcept.strict")
  -->
  %r = undef

And

%r = call float @llvm.experimental.constrained.fadd.f32(float undef, float
%x, metadata !"round.dynamic", metadata !"fpexcept.strict")
  -->
  %r = undef

What do you think about it?

Thanks,
--Serge


On Wed, Jul 21, 2021 at 8:15 PM Sanjay Patel <spatel at rotateright.com>
wrote:

> Can we use the regular FP instructions (fadd, fmul, etc.) as a model?
>
> If both operands to any of the binops are undef, then the result is undef.
> So for the corresponding constrained intrinsic, if both operands are undef,
> the result is undef and the exception state is also undef:
>
>   %r = call float @llvm.experimental.constrained.fadd.f32(float undef,
> float undef, metadata !"round.dynamic", metadata
!"fpexcept.strict")
>   -->
>   %r = undef
>
>   %r = call float @llvm.experimental.constrained.fadd.f32(float undef,
> float undef, metadata !"round.dynamic", metadata
!"fpexcept.maytrap")
>   -->
>   %r = undef
>
>
> If one operand is undef and the other is regular value, assume that the
> undef value takes on some encoding of SNaN:
>
>   %r = call float @llvm.experimental.constrained.fadd.f32(float undef,
> float %x, metadata !"round.dynamic", metadata
!"fpexcept.strict")
>   -->
>   %r = call float @llvm.experimental.constrained.fadd.f32(float SNaN,
> float %x, metadata !"round.dynamic", metadata
!"fpexcept.strict") ; raise
> invalid op exception
>   (%r could be folded to QNaN here, but we can't get rid of the call,
so
> don't bother?)
>
>   %r = call float @llvm.experimental.constrained.fadd.f32(float undef,
> float %x, metadata !"round.dynamic", metadata
!"fpexcept.maytrap")
>   -->
>   %r = QNaN ; exception state does not have to be preserved
>
> Does that match the proposed behavior in https://reviews.llvm.org/D102673
(cc
> @sepavloff)?
>
> We could go further (potentially reduce to poison) if we have
> fast-math-flags on the calls -- just as we partially do with the regular
> instructions -- but it probably doesn't matter much to real code.
>
>
> On Fri, Jul 9, 2021 at 12:06 PM Kevin Neal via llvm-dev <
> llvm-dev at lists.llvm.org> wrote:
>
>> How should the constrained FP intrinsics behave when called with an
>> operand that is “undef” and the FP environment is _*not*_ the default
>> environment? I’m specifically working in the middle end passes if it
>> matters. Let me start with the assumption that the rounding mode is not
>> relevant. That still leaves the exception handling as a factor:
>>
>> With “fpexcept.maytrap” we are allowed to drop instructions that could
or
>> would cause a trap at run-time. Does this imply we can fold the entire
>> instruction to a new undef?
>>
>> With “fpexcept.strict” we are _*not*_ allowed to lose or reorder traps.
>> So how does that affect undef? What happens in the backend? Perhaps the
>> middle end should leave the instruction with the undef and let the
backend
>> do something reasonable?
>>
>> The “maytrap” case is the one I’m most interested in. An earlier
version
>> of D103169 would fold away undef constrained intrinsics in the maytrap
>> case. This was removed so it could be handled without affecting the
rest of
>> the patch I believe.
>>
>> Opinions?
>> --
>> Kevin P. Neal
>> SAS/C and SAS/C++ Compiler
>> Compute Services
>> SAS Institute, Inc.
>>
>>
>>
>>
>> _______________________________________________
>> LLVM Developers mailing list
>> llvm-dev at lists.llvm.org
>> https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL:
<http://lists.llvm.org/pipermail/llvm-dev/attachments/20210722/eff84fc1/attachment.html>