Files
swift-mirror/stdlib/public/stubs/Stubs.cpp
tbkka 97a934c412 SR-106: New floating-point description implementation (#15474)
* SR-106: New floating-point `description` implementation

This replaces the current implementation of `description` and
`debugDescription` for the standard floating-point types with a new
formatting routine based on a variation of Florian Loitsch' Grisu2
algorithm with changes suggested by Andrysco, Jhala, and Lerner's 2016
paper describing Errol3.

Unlike the earlier code based on `sprintf` with a fixed number of
digits, this version always chooses the optimal number of digits.  As
such, we can now use the exact same output for both `description` and
`debugDescription` (except of course that `debugDescription` provides
full detail for NaNs).

The implementation has been extensively commented; people familiar with
Grisu-style algorithms should find the code easy to understand.

This implementation is:

* Fast.  It uses only fixed-width integer arithmetic and has constant
  memory and time requirements.

* Simple. It is only a little more complex than Loitsch' original
  implementation of Grisu2.  The digit decomposition logic for double is
  less than 300 lines of standard C (half of which is common arithmetic
  support routines).

* Always Accurate. Converting the decimal form back to binary (using an
  accurate algorithm such as Clinger's) will always yield exactly the
  original binary value.  For the IEEE 754 formats, the round-trip will
  produce exactly the same bit pattern in memory.  This is an essential
  requirement for JSON serialization, debugging, and logging.

* Always Short.  This always selects an accurate result with the minimum
  number of decimal digits.  (So that `1.0 / 10.0` will always print
  `0.1`.)

* Always Close.  Among all accurate, short results, this always chooses
  the result that is closest to the exact floating-point value. (In case
  of an exact tie, it rounds the last digit even.)

This resolves SR-106 and related issues that have complained
about the floating-point `description` properties being inexact.

* Remove duplicate infinity handling

* Use defined(__SIZEOF_INT128__) to detect uint128_t support

* Separate `extracting` the integer part from `clearing` the integer part

The previous code was unnecessarily obfuscated by the attempt to combine
these two operations.

* Use `UINT32_MAX` to mask off 32 bits of a larger integer

* Correct the expected NaN results for 32-bit i386

* Make the C++ exceptions here consistent

Adding a C source file somehow exposed an issue in an unrelated C++ file.
Thanks to Joe Groff for the fix.

* Rename SwiftDtoa to ".cpp"

Having a C file in stdlib/public/runtime causes strange
build failures on Linux in unrelated C++ files.

As a workaround, rename SwiftDtoa.c to .cpp to see
if that avoids the problems.

* Revert "Make the C++ exceptions here consistent"

This reverts commit 6cd5c20566.
2018-04-01 16:52:48 -07:00

570 lines
16 KiB
C++

//===--- Stubs.cpp - Swift Language ABI Runtime Stubs ---------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// Misc stubs for functions which should be defined in the core standard
// library, but are difficult or impossible to write in Swift at the
// moment.
//
//===----------------------------------------------------------------------===//
#if defined(__FreeBSD__)
#define _WITH_GETLINE
#endif
#if defined(_WIN32)
#define WIN32_LEAN_AND_MEAN
// Avoid defining macro max(), min() which conflict with std::max(), std::min()
#define NOMINMAX
#include <windows.h>
#else
#if !defined(__HAIKU__)
#include <sys/errno.h>
#else
#include <errno.h>
#endif
#include <sys/resource.h>
#include <unistd.h>
#endif
#include <climits>
#include <clocale>
#include <cstdarg>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#if defined(__CYGWIN__) || defined(_WIN32) || defined(__HAIKU__)
#include <sstream>
#include <cmath>
#elif defined(__ANDROID__)
// Android's libc implementation Bionic currently only supports the "C" locale
// (https://android.googlesource.com/platform/bionic/+/ndk-r11c/libc/bionic/locale.cpp#40).
// As such, we have no choice but to map functions like strtod_l, which should
// respect the given locale_t parameter, to functions like strtod, which do not.
#include <locale.h>
static double swift_strtod_l(const char *nptr, char **endptr, locale_t loc) {
return strtod(nptr, endptr);
}
static float swift_strtof_l(const char *nptr, char **endptr, locale_t loc) {
return strtof(nptr, endptr);
}
static long double swift_strtold_l(const char *nptr,
char **endptr,
locale_t loc) {
return strtod(nptr, endptr);
}
#define strtod_l swift_strtod_l
#define strtof_l swift_strtof_l
#define strtold_l swift_strtold_l
#elif defined(__linux__)
#include <locale.h>
#else
#include <xlocale.h>
#endif
#include <limits>
#include <thread>
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/Compiler.h"
#include "swift/Runtime/Debug.h"
#include "swift/Runtime/SwiftDtoa.h"
#include "swift/Basic/Lazy.h"
#include "../SwiftShims/LibcShims.h"
#include "../SwiftShims/RuntimeShims.h"
#include "../SwiftShims/RuntimeStubs.h"
static uint64_t uint64ToStringImpl(char *Buffer, uint64_t Value,
int64_t Radix, bool Uppercase,
bool Negative) {
char *P = Buffer;
uint64_t Y = Value;
if (Y == 0) {
*P++ = '0';
} else if (Radix == 10) {
while (Y) {
*P++ = '0' + char(Y % 10);
Y /= 10;
}
} else {
unsigned Radix32 = Radix;
while (Y) {
*P++ = llvm::hexdigit(Y % Radix32, !Uppercase);
Y /= Radix32;
}
}
if (Negative)
*P++ = '-';
std::reverse(Buffer, P);
return size_t(P - Buffer);
}
SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERFACE
uint64_t swift_int64ToString(char *Buffer, size_t BufferLength,
int64_t Value, int64_t Radix,
bool Uppercase) {
if ((Radix >= 10 && BufferLength < 32) || (Radix < 10 && BufferLength < 65))
swift::crash("swift_int64ToString: insufficient buffer size");
if (Radix == 0 || Radix > 36)
swift::crash("swift_int64ToString: invalid radix for string conversion");
bool Negative = Value < 0;
// Compute an absolute value safely, without using unary negation on INT_MIN,
// which is undefined behavior.
uint64_t UnsignedValue = Value;
if (Negative) {
// Assumes two's complement representation.
UnsignedValue = ~UnsignedValue + 1;
}
return uint64ToStringImpl(Buffer, UnsignedValue, Radix, Uppercase,
Negative);
}
SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERFACE
uint64_t swift_uint64ToString(char *Buffer, intptr_t BufferLength,
uint64_t Value, int64_t Radix,
bool Uppercase) {
if ((Radix >= 10 && BufferLength < 32) || (Radix < 10 && BufferLength < 64))
swift::crash("swift_int64ToString: insufficient buffer size");
if (Radix == 0 || Radix > 36)
swift::crash("swift_int64ToString: invalid radix for string conversion");
return uint64ToStringImpl(Buffer, Value, Radix, Uppercase,
/*Negative=*/false);
}
#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__ANDROID__)
static inline locale_t getCLocale() {
// On these platforms convenience functions from xlocale.h interpret nullptr
// as C locale.
return nullptr;
}
#elif defined(__CYGWIN__) || defined(_WIN32) || defined(__HAIKU__)
// In Cygwin, getCLocale() is not used.
#else
static locale_t makeCLocale() {
locale_t CLocale = newlocale(LC_ALL_MASK, "C", nullptr);
if (!CLocale) {
swift::crash("makeCLocale: newlocale() returned a null pointer");
}
return CLocale;
}
static locale_t getCLocale() {
return SWIFT_LAZY_CONSTANT(makeCLocale());
}
#endif
#if defined(__APPLE__)
#define swift_snprintf_l snprintf_l
#elif defined(__CYGWIN__) || defined(_WIN32) || defined(__HAIKU__)
// In Cygwin, swift_snprintf_l() is not used.
#else
static int swift_snprintf_l(char *Str, size_t StrSize, locale_t Locale,
const char *Format, ...) {
if (Locale == nullptr) {
Locale = getCLocale();
}
locale_t OldLocale = uselocale(Locale);
va_list Args;
va_start(Args, Format);
int Result = std::vsnprintf(Str, StrSize, Format, Args);
va_end(Args);
uselocale(OldLocale);
return Result;
}
#endif
#if !SWIFT_DTOA_FLOAT80_SUPPORT
template <typename T>
static uint64_t swift_floatingPointToString(char *Buffer, size_t BufferLength,
T Value, const char *Format,
bool Debug) {
if (BufferLength < 32)
swift::crash("swift_floatingPointToString: insufficient buffer size");
int Precision = std::numeric_limits<T>::digits10;
if (Debug) {
Precision = std::numeric_limits<T>::max_digits10;
}
#if defined(__CYGWIN__) || defined(_WIN32) || defined(__HAIKU__)
// Cygwin does not support uselocale(), but we can use the locale feature
// in stringstream object.
std::ostringstream ValueStream;
ValueStream.width(0);
ValueStream.precision(Precision);
ValueStream.imbue(std::locale::classic());
ValueStream << Value;
std::string ValueString(ValueStream.str());
size_t i = ValueString.length();
if (i < BufferLength) {
std::copy(ValueString.begin(), ValueString.end(), Buffer);
Buffer[i] = '\0';
} else {
swift::crash("swift_floatingPointToString: insufficient buffer size");
}
#else
// Pass a null locale to use the C locale.
int i = swift_snprintf_l(Buffer, BufferLength, /*Locale=*/nullptr, Format,
Precision, Value);
if (i < 0)
swift::crash(
"swift_floatingPointToString: unexpected return value from sprintf");
if (size_t(i) >= BufferLength)
swift::crash("swift_floatingPointToString: insufficient buffer size");
#endif
// Add ".0" to a float that (a) is not in scientific notation, (b) does not
// already have a fractional part, (c) is not infinite, and (d) is not a NaN
// value.
if (strchr(Buffer, 'e') == nullptr && strchr(Buffer, '.') == nullptr &&
strchr(Buffer, 'n') == nullptr) {
Buffer[i++] = '.';
Buffer[i++] = '0';
}
return i;
}
#endif
SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERFACE
uint64_t swift_float32ToString(char *Buffer, size_t BufferLength,
float Value, bool Debug) {
return swift_format_float(Value, Buffer, BufferLength);
}
SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERFACE
uint64_t swift_float64ToString(char *Buffer, size_t BufferLength,
double Value, bool Debug) {
return swift_format_double(Value, Buffer, BufferLength);
}
SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERFACE
uint64_t swift_float80ToString(char *Buffer, size_t BufferLength,
long double Value, bool Debug) {
#if SWIFT_DTOA_FLOAT80_SUPPORT
return swift_format_float80(Value, Buffer, BufferLength);
#else
// Use this when 'long double' is not true Float80
return swift_floatingPointToString<long double>(Buffer, BufferLength, Value,
"%0.*Lg", Debug);
#endif
}
/// \param[out] LinePtr Replaced with the pointer to the malloc()-allocated
/// line. Can be NULL if no characters were read. This buffer should be
/// freed by the caller if this function returns a positive value.
///
/// \returns Size of character data returned in \c LinePtr, or -1
/// if an error occurred, or EOF was reached.
swift::__swift_ssize_t
swift::swift_stdlib_readLine_stdin(unsigned char **LinePtr) {
#if defined(_WIN32)
if (LinePtr == nullptr)
return -1;
ssize_t Capacity = 0;
ssize_t Pos = 0;
unsigned char *ReadBuf = nullptr;
_lock_file(stdin);
for (;;) {
int ch = _fgetc_nolock(stdin);
if (ferror(stdin) || (ch == EOF && Pos == 0)) {
if (ReadBuf)
free(ReadBuf);
_unlock_file(stdin);
return -1;
}
if (Capacity - Pos <= 1) {
// Capacity changes to 128, 128*2, 128*4, 128*8, ...
Capacity = Capacity ? Capacity * 2 : 128;
unsigned char *NextReadBuf =
static_cast<unsigned char *>(realloc(ReadBuf, Capacity));
if (NextReadBuf == nullptr) {
if (ReadBuf)
free(ReadBuf);
_unlock_file(stdin);
return -1;
}
ReadBuf = NextReadBuf;
}
if (ch == EOF)
break;
ReadBuf[Pos++] = ch;
if (ch == '\n')
break;
}
ReadBuf[Pos] = '\0';
*LinePtr = ReadBuf;
_unlock_file(stdin);
return Pos;
#else
size_t Capacity = 0;
return getline((char **)LinePtr, &Capacity, stdin);
#endif
}
// Although this builtin is provided by clang rt builtins,
// it isn't provided by libgcc, which is the default
// runtime library on Linux, even when compiling with clang.
// This implementation is copied here to avoid a new dependency
// on compiler-rt on Linux.
// FIXME: rdar://14883575 Libcompiler_rt omits muloti4
#if (defined(__linux__) && defined(__x86_64__)) || \
(defined(__linux__) && defined(__aarch64__)) || \
(defined(__linux__) && defined(__powerpc64__)) || \
(defined(__linux__) && defined(__s390x__)) || \
(defined(__ANDROID__) && defined(__arm64__))
typedef int ti_int __attribute__((__mode__(TI)));
SWIFT_RUNTIME_STDLIB_INTERFACE
ti_int
__muloti4(ti_int a, ti_int b, int* overflow)
{
const int N = (int)(sizeof(ti_int) * CHAR_BIT);
const ti_int MIN = (ti_int)1 << (N-1);
const ti_int MAX = ~MIN;
*overflow = 0;
ti_int result = a * b;
if (a == MIN)
{
if (b != 0 && b != 1)
*overflow = 1;
return result;
}
if (b == MIN)
{
if (a != 0 && a != 1)
*overflow = 1;
return result;
}
ti_int sa = a >> (N - 1);
ti_int abs_a = (a ^ sa) - sa;
ti_int sb = b >> (N - 1);
ti_int abs_b = (b ^ sb) - sb;
if (abs_a < 2 || abs_b < 2)
return result;
if (sa == sb)
{
if (abs_a > MAX / abs_b)
*overflow = 1;
}
else
{
if (abs_a > MIN / -abs_b)
*overflow = 1;
}
return result;
}
#endif
// FIXME: ideally we would have a slow path here for Windows which would be
// lowered to instructions as though MSVC had generated. There does not seem to
// be a MSVC provided multiply with overflow detection that I can see, but this
// avoids an unnecessary dependency on compiler-rt for a single function.
#if (defined(__linux__) && defined(__arm__)) || defined(_WIN32)
// Similar to above, but with mulodi4. Perhaps this is
// something that shouldn't be done, and is a bandaid over
// some other lower-level architecture issue that I'm
// missing. Perhaps relevant bug report:
// FIXME: https://llvm.org/bugs/show_bug.cgi?id=14469
#if __has_attribute(__mode__)
#define SWIFT_MODE_DI __attribute__((__mode__(DI)))
#else
#define SWIFT_MODE_DI
#endif
typedef int di_int SWIFT_MODE_DI;
SWIFT_RUNTIME_STDLIB_INTERFACE
di_int
__mulodi4(di_int a, di_int b, int* overflow)
{
const int N = (int)(sizeof(di_int) * CHAR_BIT);
const di_int MIN = (di_int)1 << (N-1);
const di_int MAX = ~MIN;
*overflow = 0;
di_int result = a * b;
if (a == MIN)
{
if (b != 0 && b != 1)
*overflow = 1;
return result;
}
if (b == MIN)
{
if (a != 0 && a != 1)
*overflow = 1;
return result;
}
di_int sa = a >> (N - 1);
di_int abs_a = (a ^ sa) - sa;
di_int sb = b >> (N - 1);
di_int abs_b = (b ^ sb) - sb;
if (abs_a < 2 || abs_b < 2)
return result;
if (sa == sb)
{
if (abs_a > MAX / abs_b)
*overflow = 1;
}
else
{
if (abs_a > MIN / -abs_b)
*overflow = 1;
}
return result;
}
#endif
#if defined(__CYGWIN__) || defined(_WIN32)
#define strcasecmp _stricmp
#endif
static bool swift_stringIsSignalingNaN(const char *nptr) {
if (nptr[0] == '+' || nptr[0] == '-') {
nptr++;
}
return strcasecmp(nptr, "snan") == 0;
}
#if defined(__CYGWIN__) || defined(_WIN32) || defined(__HAIKU__)
// Cygwin does not support uselocale(), but we can use the locale feature
// in stringstream object.
template <typename T>
static const char *_swift_stdlib_strtoX_clocale_impl(
const char *nptr, T *outResult) {
if (swift_stringIsSignalingNaN(nptr)) {
*outResult = std::numeric_limits<T>::signaling_NaN();
return nptr + std::strlen(nptr);
}
std::istringstream ValueStream(nptr);
ValueStream.imbue(std::locale::classic());
T ParsedValue;
ValueStream >> ParsedValue;
*outResult = ParsedValue;
int pos = ValueStream.tellg();
if (ValueStream.eof())
pos = strlen(nptr);
if (pos <= 0)
return nullptr;
return nptr + pos;
}
const char *swift::_swift_stdlib_strtold_clocale(
const char *nptr, void *outResult) {
return _swift_stdlib_strtoX_clocale_impl(
nptr, static_cast<long double*>(outResult));
}
const char *swift::_swift_stdlib_strtod_clocale(
const char * nptr, double *outResult) {
return _swift_stdlib_strtoX_clocale_impl(nptr, outResult);
}
const char *swift::_swift_stdlib_strtof_clocale(
const char * nptr, float *outResult) {
return _swift_stdlib_strtoX_clocale_impl(nptr, outResult);
}
#else
// We can't return Float80, but we can receive a pointer to one, so
// switch the return type and the out parameter on strtold.
template <typename T>
static const char *_swift_stdlib_strtoX_clocale_impl(
const char * nptr, T* outResult, T huge,
T (*posixImpl)(const char *, char **, locale_t)
) {
if (swift_stringIsSignalingNaN(nptr)) {
// TODO: ensure that the returned sNaN bit pattern matches that of sNaNs
// produced by Swift.
*outResult = std::numeric_limits<T>::signaling_NaN();
return nptr + std::strlen(nptr);
}
char *EndPtr;
errno = 0;
const auto result = posixImpl(nptr, &EndPtr, getCLocale());
*outResult = result;
if (result == huge || result == -huge || result == 0.0 || result == -0.0) {
if (errno == ERANGE)
EndPtr = nullptr;
}
return EndPtr;
}
const char *swift::_swift_stdlib_strtold_clocale(
const char * nptr, void *outResult) {
return _swift_stdlib_strtoX_clocale_impl(
nptr, static_cast<long double*>(outResult), HUGE_VALL, strtold_l);
}
const char *swift::_swift_stdlib_strtod_clocale(
const char * nptr, double *outResult) {
return _swift_stdlib_strtoX_clocale_impl(
nptr, outResult, HUGE_VAL, strtod_l);
}
const char *swift::_swift_stdlib_strtof_clocale(
const char * nptr, float *outResult) {
return _swift_stdlib_strtoX_clocale_impl(
nptr, outResult, HUGE_VALF, strtof_l);
}
#endif
void swift::_swift_stdlib_flockfile_stdout() {
#if defined(_WIN32)
_lock_file(stdout);
#else
flockfile(stdout);
#endif
}
void swift::_swift_stdlib_funlockfile_stdout() {
#if defined(_WIN32)
_unlock_file(stdout);
#else
funlockfile(stdout);
#endif
}
int swift::_swift_stdlib_putc_stderr(int C) {
return putc(C, stderr);
}
size_t swift::_swift_stdlib_getHardwareConcurrency() {
return std::thread::hardware_concurrency();
}