2022-11-01 14:12:14 +00:00
|
|
|
# Complex Operations
|
|
|
|
|
2023-10-05 14:40:59 +02:00
|
|
|
```{contents}
|
|
|
|
---
|
|
|
|
local:
|
|
|
|
---
|
2022-11-01 14:12:14 +00:00
|
|
|
```
|
|
|
|
|
|
|
|
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**
|
2023-10-07 16:04:26 +02:00
|
|
|
```
|
2022-11-01 14:12:14 +00:00
|
|
|
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
|
2023-10-05 14:40:59 +02:00
|
|
|
requested lowering manually emits calls to libm, rather than going through the
|
2022-11-01 14:12:14 +00:00
|
|
|
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.
|
|
|
|
|
|
|
|
[1]: https://discourse.llvm.org/t/rfc-change-lowering-of-fortran-math-intrinsics/63971
|