flang-compiler / f18-llvm-project

Fork of llvm/llvm-project for f18. In sync with f18-mlir and f18.
http://llvm.org
28 stars 16 forks source link

Fix output functions used for printing half-precision, quad-precision, 80 bit float/complex and 128 bit integers #1168

Open kiranchandramohan opened 2 years ago

kiranchandramohan commented 2 years ago

For the first-cut implementation of printing various data types, we relied on upcasting or truncating various non-standard data types. At the moment we have the following (based on my understanding of getOutputFunc in flang/lib/Lower/IO.cpp)

  1. for integer(kind=16), real(kind=16) and complex(kind=16) we truncate and print using the output function for real(kind=8). The generated code does not compile for complex(kind=16) due to missing converts but silently works for the other types. The specific case of complex(kind=16) is described in issue https://github.com/flang-compiler/f18-llvm-project/issues/1144.
  2. for real(kind=2) and complex(kind=2), we upcast and print using the output function for real(kind=4)
  3. for integer(kind=2), integer(kind=4), we upcast and print using the output function for integer(kind=8)
  4. real and complex kinds 3 and 10 are not currently handled

The truncation caused by (1) will lead to incorrect behaviour due to truncation. The upcasting caused by (2) and (3) is probably OK but there are better ways to print these in the flang I/O runtime API. @klausler points out that convenient APIs for these types are not available since these types are not universally supported in C++. @klausler and @schweitzpgi suggest implementing the output functions for these types using the descriptor-based APIs.

Relevant snippet of getOutputFunc given below.

static mlir::FuncOp getOutputFunc(mlir::Location loc,
                                  fir::FirOpBuilder &builder, mlir::Type type,
                                  bool isFormatted) {
  if (!isFormatted)
    return getIORuntimeFunc<mkIOKey(OutputDescriptor)>(loc, builder);
  if (auto ty = type.dyn_cast<mlir::IntegerType>())
    return ty.getWidth() == 1
               ? getIORuntimeFunc<mkIOKey(OutputLogical)>(loc, builder)
               : getIORuntimeFunc<mkIOKey(OutputInteger64)>(loc, builder);
  if (auto ty = type.dyn_cast<mlir::FloatType>())
    return ty.getWidth() <= 32
               ? getIORuntimeFunc<mkIOKey(OutputReal32)>(loc, builder)
               : getIORuntimeFunc<mkIOKey(OutputReal64)>(loc, builder);
  if (auto ty = type.dyn_cast<fir::ComplexType>())
    return ty.getFKind() <= 4
               ? getIORuntimeFunc<mkIOKey(OutputComplex32)>(loc, builder)
               : getIORuntimeFunc<mkIOKey(OutputComplex64)>(loc, builder);
klausler commented 2 years ago

Don't forget kinds 3 and 10 for real and complex, too.

kiranchandramohan commented 2 years ago

@klausler What is real kind 3 BTW?

klausler commented 2 years ago

It's bfloat16 -- the upper 16 bits of a 32-bit IEEE-754 value, as opposed to kind 2, which is 16-bit IEEE.

vdonaldson commented 2 years ago

The size specific integer output calls that Peter introduced are now called -

bool IONAME(OutputInteger8)(Cookie, std::int8_t);
bool IONAME(OutputInteger16)(Cookie, std::int16_t);
bool IONAME(OutputInteger32)(Cookie, std::int32_t);
bool IONAME(OutputInteger64)(Cookie, std::int64_t);
bool IONAME(OutputInteger128)(Cookie, common::int128_t);