I want to give heads up and collect feedback about the change in lowering Fortran math intrinsics.
The general idea is to represent Fortran math intrinsics as MLIR operations of existing dialects (Math/Complex) as much as possible with the purpose of exposing them to more MLIR optimizations. The current implementation lowers math intrinsics “early” into opaque calls to pgmath library APIs. Not only these calls are blocking any MLIR optimizations, but further translation to LLVM IR leaves them as pgmath library calls that are not recognized/optimized in any way by LLVM backend (though, there were attempts to add some pgmath support in lib/Analysis/TargetLibraryInfo.cpp). So the proposed change is targeting potential performance issues associated with the current lowering scheme.
The existing pgmath scheme tries to look up a pgmath library function based on a Fortran intrinsic name in one of the function tables based on fast, relaxed or precise
-mllvm -math-runtime configuration. The comprehensive list of currently supported pgmath functions can be found here. If the lookup fails, then the intrinsics is looked up in llvmIntrinsics table - if found there, then an opaque call is generated to an “llvm-mangled” LLVM intrinsic name, e.g.
llvm.fabs.f32. So to add to the already mentioned issue with the opaque calls, the current implementation also relies on the availability of LLVM backend recognizing LLVM IR calls to
llvm.fabs.f32 as LLVM intrinsic calls later.
The initial change for a “late” lowering of a subset of intrinsics is merged under an internal option in ⚙ D128385 [flang] Lower Fortran math intrinsic operations into MLIR ops or libm calls. It makes use of Math MLIR dialect operations that happen to be available to represent Fortran math intrinsics in FIR/MLIR. It also lowers Fortran intrinsics to opaque FIR calls for
-math-runtime=precise which is trying to mimic the behavior of clang’s -ffp-model=strict as to disallowing optimizations that may, for example, alter the floating point exception semantics. Overall, I believe floating point model is not represented in MLIR at this point in any way, so this change is not an improvement over the current pgmath scheme but it does not seem to be a regress either.
Moreover, the new scheme also uses some llvm-mangled opaque calls (e.g.
llvm.round.*), but in general it tries to use standard
libm names such as
sinf for the
precise model. It should be possible to replace some of these names with
libm names, but for some of them, such as floating point exponentiation with integer power argument, there are just no
Moving forward, I think we need to represent the majority of Fortran math intrinsics with MLIR operations, but there are some exceptions to this direction. For example, if there are no anticipated optimization that might happen to some math operation (e.g.
BESSEL) we may want to avoid adding an MLIR operation to represent it and lower it as an optimizable call (e.g. under
fast it may have a narrow set of side effects, and use some FastMathFlags to enable more optimizations around it). If we decide to lower such an intrinsic into a FIR call, we will also have to define the name of the library implementation, e.g. if there is no
libm implementation available, we may need to add an implementation to Fortran runtime library - details of how/whether such calls must be optimized (e.g. vectorized) by LLVM or any other backend may be discussed elsewhere.
As already mentioned, one of the problems arising with representing Fortran math intrinsics with MLIR oprations is the lack of floating point model representation in MLIR. This is a related, but a different topic: neither the pgmath nor the new lowering schemes address it consistently besides relying on the opaqueness of call operations. The same issues also exists with the usage of
airth dialect FP operations that may be InstCombined, DCEd, etc. by LLVM backend. altering floating point exception behavior.
Given all the existing problems/immaturity of MLIR framework, I still think we should use MLIR operations as much as possible to represent Fortran math intrinsics. So I have started adding Math dialect operations that are missing. Below you will find a list of related differentials, e.g.:
- Adding Math
IPowIoperations for the exponentiation operator.
- Getting rid of
llvmIntrinsicstable to avoid unnecessary work duplication when we have to add new intrinsics into both
- Switching to “late” lowering by default while keeping pgmath fall back for cases that are currently not supported (e.g. complex data types are not covered at all in the new scheme).
All the current differentials are mostly targeting the
relaxed modes and aim to get somewhat performant code with LLVM backend. I believe the
strict mode cannot be reliably supported without cross-dialect (arith/math/complex/etc.) support for floating point exception semantics, rounding mode, denormals handling, errno, etc. modeling.
A list of related differentials so far:
[merged] ⚙ D128385 [flang] Lower Fortran math intrinsic operations into MLIR ops or libm calls.
⚙ D130048 [flang] Support late math lowering for intrinsics from the llvm table.
⚙ D130129 [flang] Try to lower math intrinsics to math operations first.
[merged] ⚙ D128454 [mlir][math] Lower atan to libm
[merged] ⚙ D129539 [mlir][math] Added math::tan operation.
⚙ D130035 [flang] Run algebraic simplification optimization pass.
⚙ D129809 [mlir][math] Added basic support for IPowI operation.
⚙ D129810 [mlir][math] Added math::IPowI conversion to calls of outlined implementations.
⚙ D129811 [mlir][math] Added basic support for FPowI operation.
⚙ D129812 [WIP][mlir][math] Added math::FPowI conversion to LLVM dialect.