[PATCH] D72930: [FEnv] Constfold some unary constrained operations

Serge Pavlov via Phabricator via llvm-commits llvm-commits at lists.llvm.org
Thu Feb 27 08:53:08 PST 2020


sepavloff marked an inline comment as done.
sepavloff added inline comments.


================
Comment at: llvm/lib/Analysis/ConstantFolding.cpp:1822
+      if (U.isFinite()) {
+        U.roundToIntegral(*RM);
+      } else if (U.isSignaling()) {
----------------
andrew.w.kaylor wrote:
> sepavloff wrote:
> > andrew.w.kaylor wrote:
> > > For all of these except nearbyint, you need to check the return code and only remove the call if no flags are raised or exceptionBehavior is ignore. The rest of the functions raise the inexact flag if the value is not already an integer.
> > > 
> > > We could both keep the call and RAUW the return value. We need the exception to be raised, but the constant folding could unlock additional optimizations. It might be useful to introduce new intrinsics like llvm.raiseinexact() so that we could later combine multiple calls raising the same flag.
> > > For all of these except nearbyint, you need to check the return code
> > None of rounding functions raises exception, if argument is not SNaN. All big numbers are already integers, so overflow cannot occur. Result of rounding is integer number, so underflow or inexact exceptions also absent. Mentioning of inexact exception in the description of `rint` in C standard is somewhat misleading, it is discusses earlier: https://reviews.llvm.org/D72930#inline-676354.
> My thoughts here:
> 
> (1) We aren't implementing the C standard. LLVM IR is language-independent and needs to be suitable for any language. We should clearly define the semantics of the constrained intrinsics (ideally more clearly than the C standard does for the corresponding functions), and those semantics should be suitable for any language. Depending on what we decide, that may mean languages that want stricter behavior might need to avoid using the intrinsics and instead provide a library call that does what they need.
> 
> (2) The C standard seems to be evolving on this point. The C99 standard says the rint function may raise inexact and nearbyint will not. It is silent on the other functions here. I believe the C11 standard says the rint functions "do" raise the inexact exception, and that ceil, floor, round, and trunc functions "may, but are not required to," raise inexact. Lacking information about which version of the standard the user requested, we need behavior that is suitable to any version.
> 
> (3) The point of the strict exception semantics mode is that any exception that would be raised by a literal translation of the code will be raised when the code is optimized in the strict mode. If a call to one of these functions (in the case where this was a function call) or the instructions to which they would be lowered in the case where the intrinsic is lowered directly to instructions, would raise an exception, then we should not fold the exception away in this mode.
> 
> (4) I know at least one math library implementation that raises inexact for rint, ceil, floor, round, and trunc. My earlier claim was based on writing a program and seeing what happened. The Intel math library raises exceptions for each of these functions. The x864-64 GNU math library only raises the exception for rint. Other implementations may behave differently.
> 
> I certainly understand why we would want to be able to constant fold in all these cases. However, I'm not convinced that doing so is always the right thing to do. At the very least, I think we'd need either some option to control this behavior or perhaps a new interface in TTI to query the behavior of these functions in the target math library.
This is rather complicated topic. Let's consider it in details.

**IEEE 754**

This standard defines the cases when operation should raise inexact exception (7.6):
```
Unless stated otherwise, if the rounded result of an operation is inexact - that is, it differs from what would have been computed were both exponent range and precision unbounded - then the inexact exception shall be signaled.
```
Result of rounding operation is an integer value in the same floating point format as its argument. Integers are always exact, so one could expect that inexact exception are never raised in rounding operations. However the standard treats inexactness in wider sense when dealing with rounding.

The standard defines set of rounding operations that produce values in floating point formats in 5.9:

    * roundToIntegralTiesToEven
    * roundToIntegralTowardZero
    * roundToIntegralTowardPositive
    * roundToIntegralTowardNegative
    * roundToIntegralTiesToAway

It states that:

    These operations shall not signal any exception except for signaling NaN input.

It also defines operation:
```
For the following operation, the rounding direction is the applicable rounding-direction attribute. This operation signals the invalid operation exception for a signaling NaN operand, and for a numerical operand, signals the inexact exception if the result does not have the same numerical value as x.
- sourceFormat roundToIntegralExact(source)
roundToIntegralExact(x) rounds x to an integral value according to the applicable rounding direction attribute.
```
**C standard**

Most recent draft (http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2454.pdf) defines functions `ceil`, `floor`, `round`, `roundeven` and `trunc`, that have strict correspondence with the rounding operations defined in IEEE Std 754-2008. `nearbyint` does not have strict counterpart in IEEE 754, but dynamically selects one of the standard operations depending on the current rounding mode.

As for `rint`, its description in 7.12.9.4 states that `it may signal` and does not specifies when it may signal. In the recent draft cited above (but not in the previous versions of the C standard) it is stated (F.10p6):
```
The functions bound to operations in IEC 60559 (F.3) are fully specified by IEC 60559, including rounding behaviors and floating-point exceptions.
```
F.3p1 specifies that `rint` is bound to `roundToIntegralExact`, `ceil` - to `roundToIntegralTowardPositive` and so on. 

So depending on the viewpoint, `rint` either may rise `inexact` exception (because of 7.12.9.4) or must do it (because of F.10p6). The former reflects perception of rounding as an operation, see the defect report http://www.open-std.org/jtc1/sc22/wg14/www/docs/dr_291.htm, although it probably is outdated.

**LLVM**

LLVM IR is  language-independent. Intrinsic functions `ceil`, `floor`, `round`, `roundeven` and `trunc` are defined by C standard but they correspond to rounding operations defined by IEEE 754, which is also language-independent. It looks reasonable to implement them according to their IEEEE 754 counterparts. This means that all these intrinsics but `rint` do not raise `inexact` exception, `rint` raises `inexact` exception if its result differs from the argument.

> (2) The C standard seems to be evolving on this point. The C99 standard says the rint function may raise inexact and nearbyint will not. It is silent on the other functions here. I believe the C11 standard says the rint functions "do" raise the inexact exception, and that ceil, floor, round, and trunc functions "may, but are not required to," raise inexact. Lacking information about which version of the standard the user requested, we need behavior that is suitable to any version.

Exactly. In C11 (http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf) there is a statement about `ceil` (F.10.6.1):
```
The ceil functions may, but are not required to, raise the ‘‘inexact’’ floating-point exception for finite non-integer arguments, as this implementation does.
```
Similar statements existed for `floor, `round` and `trunc`. The recent draft does not provide such option, `inexact` is not allowed for these functions. 

> (4) I know at least one math library implementation that raises inexact for rint, ceil, floor, round, and trunc. My earlier claim was based on writing a program and seeing what happened. The Intel math library raises exceptions for each of these functions. The x864-64 GNU math library only raises the exception for rint. Other implementations may behave differently.

I believe this is caused by evolution of C standard. GCC implemented special option `-fno-fp-int-builtin-inexact` deal with compatibility issues (https://gcc.gnu.org/ml/gcc-patches/2016-05/msg02060.html). We could implement this option in clang if necessary.



Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D72930/new/

https://reviews.llvm.org/D72930





More information about the llvm-commits mailing list