f18-llvm-project icon indicating copy to clipboard operation
f18-llvm-project copied to clipboard

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

Open kiranchandramohan opened this issue 3 years ago • 4 comments

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);

kiranchandramohan avatar Oct 27 '21 21:10 kiranchandramohan

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

klausler avatar Oct 27 '21 21:10 klausler

@klausler What is real kind 3 BTW?

kiranchandramohan avatar Oct 27 '21 21:10 kiranchandramohan

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

klausler avatar Oct 27 '21 21:10 klausler

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);

vdonaldson avatar Nov 08 '21 19:11 vdonaldson