diff --git a/flang/docs/ComplexOperations.md b/flang/docs/ComplexOperations.md new file mode 100644 --- /dev/null +++ b/flang/docs/ComplexOperations.md @@ -0,0 +1,76 @@ +# Complex Operations + +```eval_rst +.. 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** +```c +func.func @_QPpow_self(%arg0: !fir.ref>) -> !fir.complex<4> { + %0 = fir.alloca !fir.complex<4> + %1 = fir.load %arg0 : !fir.ref> + %2 = fir.load %arg0 : !fir.ref> + %3 = fir.convert %1 : (!fir.complex<4>) -> complex + %4 = fir.convert %2 : (!fir.complex<4>) -> complex + %5 = complex.pow %3, %4 : complex + %6 = fir.convert %5 : (complex) -> !fir.complex<4> + fir.store %6 to %0 : !fir.ref> + %7 = fir.load %0 : !fir.ref> + 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. + +[1]: https://discourse.llvm.org/t/rfc-change-lowering-of-fortran-math-intrinsics/63971