Skip to content

Commit 7e7ecef

Browse files
committed
[libc] Replace type punning with bit_cast
Although type punning is defined for union in C, it is UB in C++. This patch introduces a bit_cast function to convert between types in a safe way. This is necessary to get llvm-libc compile with GCC. This patch is extracted from D119002. Differential Revision: https://reviews.llvm.org/D119145
1 parent 9be6e40 commit 7e7ecef

File tree

19 files changed

+129
-60
lines changed

19 files changed

+129
-60
lines changed

libc/src/__support/CPP/Bit.h

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
//===-- Freestanding version of bit_cast -----------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_LIBC_SUPPORT_CPP_BIT_H
10+
#define LLVM_LIBC_SUPPORT_CPP_BIT_H
11+
12+
namespace __llvm_libc {
13+
14+
#if defined __has_builtin
15+
#if __has_builtin(__builtin_bit_cast)
16+
#define LLVM_LIBC_HAS_BUILTIN_BIT_CAST
17+
#endif
18+
#endif
19+
20+
#if defined __has_builtin
21+
#if __has_builtin(__builtin_memcpy_inline)
22+
#define LLVM_LIBC_HAS_BUILTIN_MEMCPY_INLINE
23+
#endif
24+
#endif
25+
26+
// This function guarantees the bitcast to be optimized away by the compiler for
27+
// GCC >= 8 and Clang >= 6.
28+
template <class To, class From> constexpr To bit_cast(const From &from) {
29+
static_assert(sizeof(To) == sizeof(From), "To and From must be of same size");
30+
#if defined(LLVM_LIBC_HAS_BUILTIN_BIT_CAST)
31+
return __builtin_bit_cast(To, from);
32+
#else
33+
To to;
34+
char *dst = reinterpret_cast<char *>(&to);
35+
const char *src = reinterpret_cast<const char *>(&from);
36+
#if defined(LLVM_LIBC_HAS_BUILTIN_MEMCPY_INLINE)
37+
__builtin_memcpy_inline(dst, src, sizeof(To));
38+
#else
39+
for (unsigned i = 0; i < sizeof(To); ++i)
40+
dst[i] = src[i];
41+
#endif // defined(LLVM_LIBC_HAS_BUILTIN_MEMCPY_INLINE)
42+
return to;
43+
#endif // defined(LLVM_LIBC_HAS_BUILTIN_BIT_CAST)
44+
}
45+
46+
} // namespace __llvm_libc
47+
48+
#endif // LLVM_LIBC_SUPPORT_CPP_BIT_H

libc/src/__support/CPP/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@ add_header_library(
33
HDRS
44
Array.h
55
ArrayRef.h
6+
Bit.h
67
Bitset.h
78
Functional.h
9+
Limits.h
810
StringView.h
911
TypeTraits.h
10-
Limits.h
1112
)

libc/src/__support/FPUtil/FPBits.h

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
#include "PlatformDefs.h"
1313

14+
#include "src/__support/CPP/Bit.h"
1415
#include "src/__support/CPP/TypeTraits.h"
1516

1617
#include "FloatProperties.h"
@@ -35,7 +36,7 @@ template <typename T> struct ExponentWidth {
3536
// floating numbers. On x86 platforms however, the 'long double' type maps to
3637
// an x87 floating point format. This format is an IEEE 754 extension format.
3738
// It is handled as an explicit specialization of this class.
38-
template <typename T> union FPBits {
39+
template <typename T> struct FPBits {
3940
static_assert(cpp::IsFloatingPointType<T>::Value,
4041
"FPBits instantiated with invalid type.");
4142

@@ -76,7 +77,6 @@ template <typename T> union FPBits {
7677
bool get_sign() const {
7778
return ((bits & FloatProp::SIGN_MASK) >> (FloatProp::BIT_WIDTH - 1));
7879
}
79-
T val;
8080

8181
static_assert(sizeof(T) == sizeof(UIntType),
8282
"Data type and integral representation have different sizes.");
@@ -96,15 +96,20 @@ template <typename T> union FPBits {
9696
// type match.
9797
template <typename XType,
9898
cpp::EnableIfType<cpp::IsSame<T, XType>::Value, int> = 0>
99-
explicit FPBits(XType x) : val(x) {}
99+
constexpr explicit FPBits(XType x)
100+
: bits(__llvm_libc::bit_cast<UIntType>(x)) {}
100101

101102
template <typename XType,
102103
cpp::EnableIfType<cpp::IsSame<XType, UIntType>::Value, int> = 0>
103-
explicit FPBits(XType x) : bits(x) {}
104+
constexpr explicit FPBits(XType x) : bits(x) {}
104105

105106
FPBits() : bits(0) {}
106107

107-
explicit operator T() { return val; }
108+
T get_val() const { return __llvm_libc::bit_cast<T>(bits); }
109+
110+
void set_val(T value) { bits = __llvm_libc::bit_cast<UIntType>(value); }
111+
112+
explicit operator T() const { return get_val(); }
108113

109114
UIntType uintval() const { return bits; }
110115

libc/src/__support/FPUtil/Hypot.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "BasicOperations.h"
1313
#include "FEnvImpl.h"
1414
#include "FPBits.h"
15+
#include "src/__support/CPP/Bit.h"
1516
#include "src/__support/CPP/TypeTraits.h"
1617

1718
namespace __llvm_libc {
@@ -285,7 +286,7 @@ static inline T hypot(T x, T y) {
285286
}
286287

287288
y_new |= static_cast<UIntType>(out_exp) << MantissaWidth<T>::VALUE;
288-
return *reinterpret_cast<T *>(&y_new);
289+
return __llvm_libc::bit_cast<T>(y_new);
289290
}
290291

291292
} // namespace fputil

libc/src/__support/FPUtil/ManipulationFunctions.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "NormalFloat.h"
1515
#include "PlatformDefs.h"
1616

17+
#include "src/__support/CPP/Bit.h"
1718
#include "src/__support/CPP/TypeTraits.h"
1819

1920
#include <limits.h>
@@ -171,7 +172,7 @@ static inline T nextafter(T from, T to) {
171172
int_val = (to_bits.uintval() & sign_mask) + UIntType(1);
172173
}
173174

174-
return *reinterpret_cast<T *>(&int_val);
175+
return __llvm_libc::bit_cast<T>(int_val);
175176
// TODO: Raise floating point exceptions as required by the standard.
176177
}
177178

libc/src/__support/FPUtil/generic/sqrt.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#define LLVM_LIBC_SRC_SUPPORT_FPUTIL_GENERIC_SQRT_H
1111

1212
#include "sqrt_80_bit_long_double.h"
13+
#include "src/__support/CPP/Bit.h"
1314
#include "src/__support/CPP/TypeTraits.h"
1415
#include "src/__support/FPUtil/FEnvImpl.h"
1516
#include "src/__support/FPUtil/FPBits.h"
@@ -203,7 +204,7 @@ sqrt(T x) {
203204
break;
204205
}
205206

206-
return *reinterpret_cast<T *>(&y);
207+
return __llvm_libc::bit_cast<T>(y);
207208
}
208209
}
209210
}

libc/src/__support/FPUtil/x86_64/LongDoubleBits.h

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#ifndef LLVM_LIBC_SRC_SUPPORT_FPUTIL_X86_64_LONG_DOUBLE_BITS_H
1010
#define LLVM_LIBC_SRC_SUPPORT_FPUTIL_X86_64_LONG_DOUBLE_BITS_H
1111

12+
#include "src/__support/CPP/Bit.h"
1213
#include "src/__support/architectures.h"
1314

1415
#if !defined(LLVM_LIBC_ARCH_X86)
@@ -30,7 +31,7 @@ template <> struct Padding<4> { static constexpr unsigned VALUE = 16; };
3031
// x86_64 padding.
3132
template <> struct Padding<8> { static constexpr unsigned VALUE = 48; };
3233

33-
template <> union FPBits<long double> {
34+
template <> struct FPBits<long double> {
3435
using UIntType = __uint128_t;
3536

3637
static constexpr int EXPONENT_BIAS = 0x3FFF;
@@ -91,13 +92,11 @@ template <> union FPBits<long double> {
9192
return ((bits & FloatProp::SIGN_MASK) >> (FloatProp::BIT_WIDTH - 1));
9293
}
9394

94-
long double val;
95-
9695
FPBits() : bits(0) {}
9796

9897
template <typename XType,
9998
cpp::EnableIfType<cpp::IsSame<long double, XType>::Value, int> = 0>
100-
explicit FPBits(XType x) : val(x) {
99+
explicit FPBits(XType x) : bits(__llvm_libc::bit_cast<UIntType>(x)) {
101100
// bits starts uninitialized, and setting it to a long double only
102101
// overwrites the first 80 bits. This clears those upper bits.
103102
bits = bits & ((UIntType(1) << 80) - 1);
@@ -107,7 +106,7 @@ template <> union FPBits<long double> {
107106
cpp::EnableIfType<cpp::IsSame<XType, UIntType>::Value, int> = 0>
108107
explicit FPBits(XType x) : bits(x) {}
109108

110-
operator long double() { return val; }
109+
operator long double() { return __llvm_libc::bit_cast<long double>(bits); }
111110

112111
UIntType uintval() {
113112
// We zero the padding bits as they can contain garbage.

libc/src/__support/FPUtil/x86_64/NextAfterLongDouble.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#error "Invalid include"
1616
#endif
1717

18+
#include "src/__support/CPP/Bit.h"
1819
#include "src/__support/FPUtil/FPBits.h"
1920

2021
#include <stdint.h>
@@ -111,7 +112,7 @@ static inline long double nextafter(long double from, long double to) {
111112
}
112113
}
113114

114-
return *reinterpret_cast<long double *>(&int_val);
115+
return __llvm_libc::bit_cast<long double>(int_val);
115116
// TODO: Raise floating point exceptions as required by the standard.
116117
}
117118

libc/src/__support/FPUtil/x86_64/sqrt.h

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,8 @@ template <> inline double sqrt<double>(double x) {
3333
}
3434

3535
template <> inline long double sqrt<long double>(long double x) {
36-
long double result;
37-
__asm__ __volatile__("fsqrt" : "=t"(result) : "t"(x));
38-
return result;
36+
__asm__ __volatile__("fsqrt" : "+t"(x));
37+
return x;
3938
}
4039

4140
} // namespace fputil

libc/src/math/generic/log10f.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ LLVM_LIBC_FUNCTION(float, log10f, (float x)) {
155155
return x;
156156
}
157157
// Normalize denormal inputs.
158-
xbits.val *= 0x1.0p23f;
158+
xbits.set_val(xbits.get_val() * 0x1.0p23f);
159159
m -= 23.0;
160160
}
161161

@@ -164,7 +164,7 @@ LLVM_LIBC_FUNCTION(float, log10f, (float x)) {
164164
xbits.set_unbiased_exponent(0x7F);
165165
int f_index = xbits.get_mantissa() >> 16;
166166

167-
FPBits f(xbits.val);
167+
FPBits f = xbits;
168168
f.bits &= ~0x0000'FFFF;
169169

170170
double d = static_cast<float>(xbits) - static_cast<float>(f);

libc/src/math/generic/log1pf.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ INLINE_FMA static inline float log(double x) {
5959
int f_index =
6060
xbits.get_mantissa() >> 45; // fputil::MantissaWidth<double>::VALUE - 7
6161

62-
FPBits f(xbits.val);
62+
FPBits f = xbits;
6363
// Clear the lowest 45 bits.
6464
f.bits &= ~0x0000'1FFF'FFFF'FFFFULL;
6565

libc/src/math/generic/log2f.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ LLVM_LIBC_FUNCTION(float, log2f, (float x)) {
138138
return x;
139139
}
140140
// Normalize denormal inputs.
141-
xbits.val *= 0x1.0p23f;
141+
xbits.set_val(xbits.get_val() * 0x1.0p23f);
142142
m = -23;
143143
}
144144

@@ -149,7 +149,7 @@ LLVM_LIBC_FUNCTION(float, log2f, (float x)) {
149149
// lookup tables.
150150
int f_index = xbits.get_mantissa() >> 16;
151151

152-
FPBits f(xbits.val);
152+
FPBits f = xbits;
153153
// Clear the lowest 16 bits.
154154
f.bits &= ~0x0000'FFFF;
155155

libc/src/math/generic/logf.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ LLVM_LIBC_FUNCTION(float, logf, (float x)) {
104104
return x;
105105
}
106106
// Normalize denormal inputs.
107-
xbits.val *= 0x1.0p23f;
107+
xbits.set_val(xbits.get_val() * 0x1.0p23f);
108108
m = -23;
109109
}
110110

@@ -113,7 +113,7 @@ LLVM_LIBC_FUNCTION(float, logf, (float x)) {
113113
xbits.set_unbiased_exponent(0x7F);
114114
int f_index = xbits.get_mantissa() >> 16;
115115

116-
FPBits f(xbits.val);
116+
FPBits f = xbits;
117117
f.bits &= ~0x0000'FFFF;
118118

119119
double d = static_cast<float>(xbits) - static_cast<float>(f);

libc/src/math/generic/math_utils.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#ifndef LLVM_LIBC_SRC_MATH_MATH_UTILS_H
1010
#define LLVM_LIBC_SRC_MATH_MATH_UTILS_H
1111

12+
#include "src/__support/CPP/Bit.h"
1213
#include "src/__support/CPP/TypeTraits.h"
1314
#include "src/__support/common.h"
1415
#include <errno.h>
@@ -19,19 +20,19 @@
1920
namespace __llvm_libc {
2021

2122
static inline uint32_t as_uint32_bits(float x) {
22-
return *reinterpret_cast<uint32_t *>(&x);
23+
return __llvm_libc::bit_cast<uint32_t>(x);
2324
}
2425

2526
static inline uint64_t as_uint64_bits(double x) {
26-
return *reinterpret_cast<uint64_t *>(&x);
27+
return __llvm_libc::bit_cast<uint64_t>(x);
2728
}
2829

2930
static inline float as_float(uint32_t x) {
30-
return *reinterpret_cast<float *>(&x);
31+
return __llvm_libc::bit_cast<float>(x);
3132
}
3233

3334
static inline double as_double(uint64_t x) {
34-
return *reinterpret_cast<double *>(&x);
35+
return __llvm_libc::bit_cast<double>(x);
3536
}
3637

3738
static inline uint32_t top12_bits(float x) { return as_uint32_bits(x) >> 20; }

libc/src/string/memory_utils/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ add_header_library(
77
memcmp_implementations.h
88
memcpy_implementations.h
99
memset_implementations.h
10+
DEPS
11+
standalone_cpp
1012
)
1113

1214
add_header_library(

0 commit comments

Comments
 (0)