Some clarification after getting feedback from Craig Topper….
It’s probably best to say in the documentation that the llvm.nearbyint and llvm.rint functions “assume the default rounding mode, roundToNearest”. This will allow the optimizer to transform them as if they were rounding to nearest without requiring backends to use an encoding that enforces roundToNearest as the rounding mode for these operations. On modern x86 targets we can encode it either way, but it seems more consistent to continue using the current encoding which tells the processor to use the current rounding mode. For other targets (including cases where x86 is forced to use x87 instructions), it may be much easier to leave this at the discretion of the backend.
Also, we should take care to document the non-constrained forms of these intrinsics in a way that makes clear that we are “assuming” and not requiring that the operation has no side effects. For the constrained version of nearbyint, we will require that the inexact exception is not raised (to be consistent with iEEE 754-2019’s roundToIntegral operations) and for the constrained version of rint we will require that the inexact exception is raised (to be consistent with iEEE 754-2019’s roundToIntegralExact operation), but for the non-constrained forms it should be clear that the backend is free to implement this in the most efficient way possible, without regard to FP exception behavior.
Finally, I see now the problem with documenting these in terms of the IEEE operations, given that IEEE 754-2019 doesn’t describe an operation that uses the current rounding mode without knowing what that is. I see this as a problem of documentation rather than one that presents any difficulty for the implementation.
Here are some suggested wordings for the “Semantics” section of the langref for these functions:
llvm.nearbyint::semantics
This function returns the same value as one of the IEEE 754-2019 roundToIntegral operations using the current rounding mode. The optimizer may assume that actual rounding mode is roundToNearest (IEEE 754: roundTiesToEven), but backends may encode this operation either using that rounding mode explicitly or using the dynamic rounding mode from the floating point environment. The optimizer may assume that the operation has no side effects and raises no FP exceptions, but backends may encode this operation using either instructions that raise exceptions or instructions that do not. The FP exceptions are assumed to be ignored.
llvm.rint (delete, or identical semantics to llvm.nearbyint)
llvm.experimental.constrained.nearbyint::semantics
This function returns the same value as one of the IEEE 754-2019 roundToIntegral operations. If the roundingMode argument is fpround.dynamic, the behavior corresponds to whichever of the roundToIntegral operations matches the dynamic rounding mode when the operation is executed. The optimizer may not assume any rounding mode in this case, and backends must encode the operation in a way that uses the dynamic rounding mode. Otherwise, the rounding mode may be assumed to be that described by the roundingMode argument and backends may either use instructions that encode that rounding mode explicitly or use the current rounding mode from the FP environment.
The optimizer may assume that this operation does not raise the inexact exception when the return value differs from the input value, and if the exceptionBehavior argument is not fpexcept.ignore, the backend must encode this operation using instructions that guarantee that the inexact exception is not raised. If the exceptionBehavior argument is fpexcept.ignore, backends may encode this operation using either instructions that raise exceptions or instructions that do not.
llvm.experimental.constrained.rint::semantics
This function returns the same value as the IEEE 754-2019 roundToIntegralExact operation. If the roundingMode argument is fpround.dynamic, the behavior uses to the dynamic rounding mode when the operation is executed. The optimizer may not assume any rounding mode in this case, and backends must encode the operation in a way that uses the dynamic rounding mode. Otherwise, the rounding mode may be assumed to be that described by the roundingMode argument and backends may either use instructions that encode that rounding mode explicitly or use the current rounding mode from the FP environment.
If the exceptionBehavior argument is not fpexcept.ignore, the optimizer must assume that this operation will raise the inexact exception when the return value differs from the input value and the backend must encode this operation using instructions that guarantee that the inexact exception is raised in that case. If the exceptionBehavior argument is fpexcept.ignore, backends may encode this operation using either instructions that raise exceptions or instructions that do not.
I’d like to also say that these intrinsics can be lowered to the corresponding libm functions, but I’m not sure all libm implementations meet the requirements above.
-Andy