forked from OSchip/llvm-project
[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:
parent
6ee2aa126c
commit
f65f830e5f
|
@ -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; }
|
||||
|
|
|
@ -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 &);
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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>;
|
||||
|
|
|
@ -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>;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue