llvm-project/flang/docs/ComplexOperations.md
Shunsuke Watanabe c9900015a9
[flang] Add -fcomplex-arithmetic= option and select complex division algorithm (#146641)
This patch adds an option to select the method for computing complex
number division. It uses `LoweringOptions` to determine whether to lower
complex division to a runtime function call or to MLIR's `complex.div`,
and `CodeGenOptions` to select the computation algorithm for
`complex.div`. The available option values and their corresponding
algorithms are as follows:
- `full`: Lower to a runtime function call. (Default behavior)
- `improved`: Lower to `complex.div` and expand to Smith's algorithm.
- `basic`: Lower to `complex.div` and expand to the algebraic algorithm.

See also the discussion in the following discourse post:
https://discourse.llvm.org/t/optimization-of-complex-number-division/83468

---------

Co-authored-by: Tarun Prabhu <tarunprabhu@gmail.com>
2025-07-09 13:43:54 +09:00

100 lines
4.3 KiB
Markdown

# Complex Operations
```{contents}
---
local:
---
```
Fortran includes support for complex number types and a set of operators and
intrinsics that work on these types. Some of those operations are complicated
and require runtime function calls to implement.
This document outlines a design for generating these operations using the MLIR
complex dialect while avoiding cross-platform ABI issues.
## FIR Representation
MLIR contains a complex dialect, similar to the Math dialect also used for
lowering some integer and floating point operations in Flang. Conversion between
fir.complex types and MLIR complex types is supported.
As a result at the FIR level, complex operations can be represented as
conversions from the fir.complex type to the equivalent MLIR complex type, use
of the MLIR operation and a conversion back.
This is similar to the way the math intrinsics are lowered, as proposed [here][1]
**Fortran**
```fortran
function pow_self(c)
complex, intent(in) :: c
complex :: pow_self
pow_self = c ** c
end function pow_self
```
**FIR**
```
func.func @_QPpow_self(%arg0: !fir.ref<!fir.complex<4>>) -> !fir.complex<4> {
%0 = fir.alloca !fir.complex<4>
%1 = fir.load %arg0 : !fir.ref<!fir.complex<4>>
%2 = fir.load %arg0 : !fir.ref<!fir.complex<4>>
%3 = fir.convert %1 : (!fir.complex<4>) -> complex<f32>
%4 = fir.convert %2 : (!fir.complex<4>) -> complex<f32>
%5 = complex.pow %3, %4 : complex<f32>
%6 = fir.convert %5 : (complex<f32>) -> !fir.complex<4>
fir.store %6 to %0 : !fir.ref<!fir.complex<4>>
%7 = fir.load %0 : !fir.ref<!fir.complex<4>>
return %7 : !fir.complex<4>
}
```
Some operations are currently missing in the MLIR complex dialect that we would
want to use here, such as powi and the hyperbolic trigonometry functions.
For the missing operations we call directly to libm where possible, for powi
we provide an implementation in the flang runtime.
## Lowering
The MLIR complex dialect supports lowering either by emitting calls to the
complex functions in libm (ComplexToLibm), or through lowering to the standard
dialect (ComplexToStandard). However, as MLIR has no target awareness, the
lowering to libm functions suffers from ABI incompatibilities on some platforms.
As such the custom lowering to the standard dialect is used. This may be
something to revisit in future if performance could be improved by using the
libm functions.
Similarly to the numerical lowering through the math dialect, certain MLIR
optimisations could violate the precise floating point model, so when that is
requested lowering manually emits calls to libm, rather than going through the
MLIR complex dialect.
The ComplexToStandard dialect does still call into libm for some floating
point math operations, however these don't have the same ABI issues as the
complex libm functions.
The flang driver option `-fcomplex-arithmetic=` allows you to select whether
complex number division is lowered to function calls or to the `complex.div`
operation in the MLIR complex dialect. To avoid the ABI issues mentioned above,
the choice of function calls or the `complex.div` operation is made during the
lowering phase. The behavior of this option is as follows:
- `basic`: Lowers to `complex.div` and is converted to algebraic formulas. No
special handling to avoid overflow. NaN and infinite values are not handled.
- `improved`: Lowers to `complex.div` and is converted to Smith's algorithm. See
SMITH, R. L. Algorithm 116: Complex division. Commun. ACM 5, 8 (1962). This
value offers improved handling for overflow in intermediate calculations, but
overflow may occur. NaN and infinite values are handled.
- `full`: Lowers to a call to runtime library functions. Overflow and non-finite
values are handled by the library implementation. This is the default value.
While [the same option in clang][2] allows specifying `promoted`, this is not
implemented in Flang. Also, in the case of `improved`, clang does not handle NaN
and infinite values, but Flang does. These behavioral differences arise because
the transformation of complex division calculations depends on the implementation
of ComplexToStandard, which may change in the future.
[1]: https://discourse.llvm.org/t/rfc-change-lowering-of-fortran-math-intrinsics/63971
[2]: https://clang.llvm.org/docs/UsersManual.html#cmdoption-fcomplex-arithmetic