[flang] runtime: fix output B/O/Z editing of "negative" values

B/O/Z integer output editing must not reflect any sign extension
of scalar output values.  Add more size-dependent OutputInteger
I/O APIs and kind instantiations of EditIntegerOutput.

Differential Revision: https://reviews.llvm.org/D111678
This commit is contained in:
peter klausler 2021-10-11 15:41:14 -07:00
parent 6ee2aa126c
commit f65f830e5f
7 changed files with 113 additions and 52 deletions

View File

@ -6,8 +6,8 @@
//
//===----------------------------------------------------------------------===//
// Portable 128-bit unsigned integer arithmetic for use in impoverished
// C++ implementations lacking __uint128_t.
// Portable 128-bit integer arithmetic for use in impoverished C++
// implementations lacking __uint128_t & __int128_t.
#ifndef FORTRAN_COMMON_UINT128_H_
#define FORTRAN_COMMON_UINT128_H_
@ -47,6 +47,18 @@ public:
constexpr Int128 &operator=(const Int128 &) = default;
constexpr Int128 &operator=(Int128 &&) = default;
constexpr Int128(const Int128<!IS_SIGNED> &n)
: low_{n.low()}, high_{n.high()} {}
constexpr Int128(Int128<!IS_SIGNED> &&n) : low_{n.low()}, high_{n.high()} {}
constexpr Int128 &operator=(const Int128<!IS_SIGNED> &n) {
low_ = n.low();
high_ = n.high();
}
constexpr Int128 &operator=(Int128<!IS_SIGNED> &&n) {
low_ = n.low();
high_ = n.high();
}
constexpr Int128 operator+() const { return *this; }
constexpr Int128 operator~() const { return {~high_, ~low_}; }
constexpr Int128 operator-() const { return ~*this + 1; }

View File

@ -11,6 +11,7 @@
#ifndef FORTRAN_RUNTIME_IO_API_H_
#define FORTRAN_RUNTIME_IO_API_H_
#include "flang/Common/uint128.h"
#include "flang/Runtime/entry-names.h"
#include "flang/Runtime/iostat.h"
#include <cinttypes>
@ -56,7 +57,7 @@ extern "C" {
// These functions initiate data transfer statements (READ, WRITE, PRINT).
// Example: PRINT *, 666 is implemented as the series of calls:
// Cookie cookie{BeginExternalListOutput(DefaultUnit,__FILE__,__LINE__)};
// OutputInteger64(cookie, 666);
// OutputInteger32(cookie, 666);
// EndIoStatement(cookie);
// Internal I/O initiation
@ -225,7 +226,13 @@ bool IONAME(OutputUnformattedBlock)(
bool IONAME(InputUnformattedBlock)(
Cookie, char *, std::size_t, std::size_t elementBytes);
// Formatted (including list directed) I/O data items
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);
#ifdef __SIZEOF_INT128__
bool IONAME(OutputInteger128)(Cookie, common::int128_t);
#endif
bool IONAME(InputInteger)(Cookie, std::int64_t &, int kind = 8);
bool IONAME(OutputReal32)(Cookie, float);
bool IONAME(InputReal32)(Cookie, float &);

View File

@ -42,22 +42,22 @@ inline A &ExtractElement(IoStatementState &io, const Descriptor &descriptor,
// automatic repetition counts, like "10*3.14159", for list-directed and
// NAMELIST array output.
template <typename A, Direction DIR>
template <int KIND, Direction DIR>
inline bool FormattedIntegerIO(
IoStatementState &io, const Descriptor &descriptor) {
std::size_t numElements{descriptor.Elements()};
SubscriptValue subscripts[maxRank];
descriptor.GetLowerBounds(subscripts);
using IntType = CppTypeFor<TypeCategory::Integer, KIND>;
for (std::size_t j{0}; j < numElements; ++j) {
if (auto edit{io.GetNextDataEdit()}) {
A &x{ExtractElement<A>(io, descriptor, subscripts)};
IntType &x{ExtractElement<IntType>(io, descriptor, subscripts)};
if constexpr (DIR == Direction::Output) {
if (!EditIntegerOutput(io, *edit, static_cast<std::int64_t>(x))) {
if (!EditIntegerOutput<KIND>(io, *edit, x)) {
return false;
}
} else if (edit->descriptor != DataEdit::ListDirectedNullValue) {
if (!EditIntegerInput(io, *edit, reinterpret_cast<void *>(&x),
static_cast<int>(sizeof(A)))) {
if (!EditIntegerInput(io, *edit, reinterpret_cast<void *>(&x), KIND)) {
return false;
}
}
@ -183,15 +183,16 @@ inline bool FormattedCharacterIO(
return true;
}
template <typename A, Direction DIR>
template <int KIND, Direction DIR>
inline bool FormattedLogicalIO(
IoStatementState &io, const Descriptor &descriptor) {
std::size_t numElements{descriptor.Elements()};
SubscriptValue subscripts[maxRank];
descriptor.GetLowerBounds(subscripts);
auto *listOutput{io.get_if<ListDirectedStatementState<Direction::Output>>()};
using IntType = CppTypeFor<TypeCategory::Integer, KIND>;
for (std::size_t j{0}; j < numElements; ++j) {
A &x{ExtractElement<A>(io, descriptor, subscripts)};
IntType &x{ExtractElement<IntType>(io, descriptor, subscripts)};
if (listOutput) {
if (!ListDirectedLogicalOutput(io, *listOutput, x != 0)) {
return false;
@ -377,20 +378,15 @@ static bool DescriptorIO(IoStatementState &io, const Descriptor &descriptor) {
case TypeCategory::Integer:
switch (kind) {
case 1:
return FormattedIntegerIO<CppTypeFor<TypeCategory::Integer, 1>, DIR>(
io, descriptor);
return FormattedIntegerIO<1, DIR>(io, descriptor);
case 2:
return FormattedIntegerIO<CppTypeFor<TypeCategory::Integer, 2>, DIR>(
io, descriptor);
return FormattedIntegerIO<2, DIR>(io, descriptor);
case 4:
return FormattedIntegerIO<CppTypeFor<TypeCategory::Integer, 4>, DIR>(
io, descriptor);
return FormattedIntegerIO<4, DIR>(io, descriptor);
case 8:
return FormattedIntegerIO<CppTypeFor<TypeCategory::Integer, 8>, DIR>(
io, descriptor);
return FormattedIntegerIO<8, DIR>(io, descriptor);
case 16:
return FormattedIntegerIO<CppTypeFor<TypeCategory::Integer, 16>, DIR>(
io, descriptor);
return FormattedIntegerIO<16, DIR>(io, descriptor);
default:
handler.Crash(
"DescriptorIO: Unimplemented INTEGER kind (%d) in descriptor",
@ -452,17 +448,13 @@ static bool DescriptorIO(IoStatementState &io, const Descriptor &descriptor) {
case TypeCategory::Logical:
switch (kind) {
case 1:
return FormattedLogicalIO<CppTypeFor<TypeCategory::Integer, 1>, DIR>(
io, descriptor);
return FormattedLogicalIO<1, DIR>(io, descriptor);
case 2:
return FormattedLogicalIO<CppTypeFor<TypeCategory::Integer, 2>, DIR>(
io, descriptor);
return FormattedLogicalIO<2, DIR>(io, descriptor);
case 4:
return FormattedLogicalIO<CppTypeFor<TypeCategory::Integer, 4>, DIR>(
io, descriptor);
return FormattedLogicalIO<4, DIR>(io, descriptor);
case 8:
return FormattedLogicalIO<CppTypeFor<TypeCategory::Integer, 8>, DIR>(
io, descriptor);
return FormattedLogicalIO<8, DIR>(io, descriptor);
default:
handler.Crash(
"DescriptorIO: Unimplemented LOGICAL kind (%d) in descriptor",

View File

@ -12,27 +12,27 @@
namespace Fortran::runtime::io {
template <typename INT, typename UINT>
bool EditIntegerOutput(IoStatementState &io, const DataEdit &edit, INT n) {
char buffer[130], *end = &buffer[sizeof buffer], *p = end;
bool isNegative{false};
if constexpr (std::is_same_v<INT, UINT>) {
isNegative = (n >> (8 * sizeof(INT) - 1)) != 0;
} else {
isNegative = n < 0;
}
UINT un{static_cast<UINT>(isNegative ? -n : n)};
template <int KIND>
bool EditIntegerOutput(IoStatementState &io, const DataEdit &edit,
common::HostSignedIntType<8 * KIND> n) {
char buffer[130], *end{&buffer[sizeof buffer]}, *p{end};
bool isNegative{n < 0};
using Unsigned = common::HostUnsignedIntType<8 * KIND>;
Unsigned un{static_cast<Unsigned>(n)};
int signChars{0};
switch (edit.descriptor) {
case DataEdit::ListDirected:
case 'G':
case 'I':
if (isNegative) {
un = -n;
}
if (isNegative || (edit.modes.editingFlags & signPlus)) {
signChars = 1; // '-' or '+'
}
while (un > 0) {
auto quotient{un / 10u};
*--p = '0' + static_cast<int>(un - UINT{10} * quotient);
*--p = '0' + static_cast<int>(un - 10u * quotient);
un = quotient;
}
break;
@ -382,8 +382,7 @@ bool RealOutputEditing<binaryPrecision>::EditEXOutput(const DataEdit &) {
"EX output editing is not yet implemented"); // TODO
}
template <int binaryPrecision>
bool RealOutputEditing<binaryPrecision>::Edit(const DataEdit &edit) {
template <int KIND> bool RealOutputEditing<KIND>::Edit(const DataEdit &edit) {
switch (edit.descriptor) {
case 'D':
return EditEorDOutput(edit);
@ -398,7 +397,7 @@ bool RealOutputEditing<binaryPrecision>::Edit(const DataEdit &edit) {
case 'B':
case 'O':
case 'Z':
return EditIntegerOutput(io_, edit,
return EditIntegerOutput<KIND>(io_, edit,
decimal::BinaryFloatingPointNumber<binaryPrecision>{x_}.raw());
case 'G':
return Edit(EditForGOutput(edit));
@ -503,10 +502,16 @@ bool EditDefaultCharacterOutput(IoStatementState &io, const DataEdit &edit,
io.Emit(x, std::min(width, len));
}
template bool EditIntegerOutput<std::int64_t, std::uint64_t>(
template bool EditIntegerOutput<1>(
IoStatementState &, const DataEdit &, std::int8_t);
template bool EditIntegerOutput<2>(
IoStatementState &, const DataEdit &, std::int16_t);
template bool EditIntegerOutput<4>(
IoStatementState &, const DataEdit &, std::int32_t);
template bool EditIntegerOutput<8>(
IoStatementState &, const DataEdit &, std::int64_t);
template bool EditIntegerOutput<common::uint128_t, common::uint128_t>(
IoStatementState &, const DataEdit &, common::uint128_t);
template bool EditIntegerOutput<16>(
IoStatementState &, const DataEdit &, common::int128_t);
template class RealOutputEditing<2>;
template class RealOutputEditing<3>;

View File

@ -29,8 +29,9 @@ namespace Fortran::runtime::io {
// The DataEdit reference is const here (and elsewhere in this header) so that
// one edit descriptor with a repeat factor may safely serve to edit
// multiple elements of an array.
template <typename INT = std::int64_t, typename UINT = std::uint64_t>
bool EditIntegerOutput(IoStatementState &, const DataEdit &, INT);
template <int KIND>
bool EditIntegerOutput(
IoStatementState &, const DataEdit &, common::HostSignedIntType<8 * KIND>);
// Encapsulates the state of a REAL output conversion.
class RealOutputEditingBase {
@ -98,10 +99,16 @@ bool ListDirectedDefaultCharacterOutput(IoStatementState &,
bool EditDefaultCharacterOutput(
IoStatementState &, const DataEdit &, const char *, std::size_t);
extern template bool EditIntegerOutput<std::int64_t, std::uint64_t>(
extern template bool EditIntegerOutput<1>(
IoStatementState &, const DataEdit &, std::int8_t);
extern template bool EditIntegerOutput<2>(
IoStatementState &, const DataEdit &, std::int16_t);
extern template bool EditIntegerOutput<4>(
IoStatementState &, const DataEdit &, std::int32_t);
extern template bool EditIntegerOutput<8>(
IoStatementState &, const DataEdit &, std::int64_t);
extern template bool EditIntegerOutput<common::uint128_t, common::uint128_t>(
IoStatementState &, const DataEdit &, common::uint128_t);
extern template bool EditIntegerOutput<16>(
IoStatementState &, const DataEdit &, common::int128_t);
extern template class RealOutputEditing<2>;
extern template class RealOutputEditing<3>;

View File

@ -949,15 +949,53 @@ bool IONAME(InputUnformattedBlock)(
return false;
}
bool IONAME(OutputInteger8)(Cookie cookie, std::int8_t n) {
cookie->CheckFormattedStmtType<Direction::Output>("OutputInteger8");
StaticDescriptor staticDescriptor;
Descriptor &descriptor{staticDescriptor.descriptor()};
descriptor.Establish(
TypeCategory::Integer, 1, reinterpret_cast<void *>(&n), 0);
return descr::DescriptorIO<Direction::Output>(*cookie, descriptor);
}
bool IONAME(OutputInteger16)(Cookie cookie, std::int16_t n) {
cookie->CheckFormattedStmtType<Direction::Output>("OutputInteger16");
StaticDescriptor staticDescriptor;
Descriptor &descriptor{staticDescriptor.descriptor()};
descriptor.Establish(
TypeCategory::Integer, 2, reinterpret_cast<void *>(&n), 0);
return descr::DescriptorIO<Direction::Output>(*cookie, descriptor);
}
bool IONAME(OutputInteger32)(Cookie cookie, std::int32_t n) {
cookie->CheckFormattedStmtType<Direction::Output>("OutputInteger32");
StaticDescriptor staticDescriptor;
Descriptor &descriptor{staticDescriptor.descriptor()};
descriptor.Establish(
TypeCategory::Integer, 4, reinterpret_cast<void *>(&n), 0);
return descr::DescriptorIO<Direction::Output>(*cookie, descriptor);
}
bool IONAME(OutputInteger64)(Cookie cookie, std::int64_t n) {
cookie->CheckFormattedStmtType<Direction::Output>("OutputInteger64");
StaticDescriptor staticDescriptor;
Descriptor &descriptor{staticDescriptor.descriptor()};
descriptor.Establish(
TypeCategory::Integer, sizeof n, reinterpret_cast<void *>(&n), 0);
TypeCategory::Integer, 8, reinterpret_cast<void *>(&n), 0);
return descr::DescriptorIO<Direction::Output>(*cookie, descriptor);
}
#ifdef __SIZEOF_INT128__
bool IONAME(OutputInteger128)(Cookie cookie, common::int128_t n) {
cookie->CheckFormattedStmtType<Direction::Output>("OutputInteger128");
StaticDescriptor staticDescriptor;
Descriptor &descriptor{staticDescriptor.descriptor()};
descriptor.Establish(
TypeCategory::Integer, 16, reinterpret_cast<void *>(&n), 0);
return descr::DescriptorIO<Direction::Output>(*cookie, descriptor);
}
#endif
bool IONAME(InputInteger)(Cookie cookie, std::int64_t &n, int kind) {
cookie->CheckFormattedStmtType<Direction::Input>("InputInteger");
StaticDescriptor staticDescriptor;

View File

@ -66,7 +66,7 @@ TEST(IOApiTests, HelloWorldOutputTest) {
// Write string, integer, and logical values to buffer
IONAME(OutputAscii)(cookie, "WORLD", 5);
IONAME(OutputInteger64)(cookie, 678);
IONAME(OutputInteger64)(cookie, 0xfeedface);
IONAME(OutputInteger32)(cookie, 0xfeedface);
IONAME(OutputLogical)(cookie, true);
// Ensure IO succeeded