Skip to content

Commit 80bf7b6

Browse files
committed
[clang][bytecode] Implement bitcasts to floating-point values (llvm#114485)
This time I tested on big-endian hosts.
1 parent 88823d0 commit 80bf7b6

File tree

4 files changed

+130
-14
lines changed

4 files changed

+130
-14
lines changed

clang/lib/AST/ByteCode/Interp.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3062,7 +3062,17 @@ inline bool BitCast(InterpState &S, CodePtr OpPC, bool TargetIsUCharOrByte,
30623062
return false;
30633063

30643064
if constexpr (std::is_same_v<T, Floating>) {
3065-
assert(false && "Implement bitcasting to a floating type");
3065+
assert(Sem);
3066+
ptrdiff_t Offset = 0;
3067+
3068+
if (llvm::sys::IsBigEndianHost) {
3069+
unsigned NumBits = llvm::APFloatBase::getSizeInBits(*Sem);
3070+
assert(NumBits % 8 == 0);
3071+
assert(NumBits <= ResultBitWidth);
3072+
Offset = (ResultBitWidth - NumBits) / 8;
3073+
}
3074+
3075+
S.Stk.push<Floating>(T::bitcastFromMemory(Buff.data() + Offset, *Sem));
30663076
} else {
30673077
assert(!Sem);
30683078
S.Stk.push<T>(T::bitcastFromMemory(Buff.data(), ResultBitWidth));

clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -315,19 +315,34 @@ static bool readPointerToBuffer(const Context &Ctx, const Pointer &FromPtr,
315315
assert(false && "Implement casting to pointer types");
316316

317317
CharUnits ObjectReprChars = ASTCtx.getTypeSizeInChars(P.getType());
318-
unsigned BitWidth;
319-
if (const FieldDecl *FD = P.getField(); FD && FD->isBitField())
320-
BitWidth = FD->getBitWidthValue(ASTCtx);
321-
else
322-
BitWidth = ASTCtx.toBits(ObjectReprChars);
323-
318+
unsigned BitWidth = ASTCtx.toBits(ObjectReprChars);
324319
llvm::SmallVector<std::byte> Buff(ObjectReprChars.getQuantity());
325-
BITCAST_TYPE_SWITCH_WITH_FLOAT(T, {
326-
T Val = P.deref<T>();
327-
Val.bitcastToMemory(Buff.data());
328-
});
329-
if (SwapData)
330-
swapBytes(Buff.data(), ObjectReprChars.getQuantity());
320+
// Work around floating point types that contain unused padding bytes.
321+
// This is really just `long double` on x86, which is the only
322+
// fundamental type with padding bytes.
323+
if (T == PT_Float) {
324+
Floating &F = P.deref<Floating>();
325+
unsigned NumBits =
326+
llvm::APFloatBase::getSizeInBits(F.getAPFloat().getSemantics());
327+
assert(NumBits % 8 == 0);
328+
assert(NumBits <= (ObjectReprChars.getQuantity() * 8));
329+
F.bitcastToMemory(Buff.data());
330+
// Now, only (maybe) swap the actual size of the float, excluding the
331+
// padding bits.
332+
if (SwapData)
333+
swapBytes(Buff.data(), NumBits / 8);
334+
335+
} else {
336+
if (const FieldDecl *FD = P.getField(); FD && FD->isBitField())
337+
BitWidth = FD->getBitWidthValue(ASTCtx);
338+
339+
BITCAST_TYPE_SWITCH(T, {
340+
T Val = P.deref<T>();
341+
Val.bitcastToMemory(Buff.data());
342+
});
343+
if (SwapData)
344+
swapBytes(Buff.data(), ObjectReprChars.getQuantity());
345+
}
331346

332347
if (BitWidth != (Buff.size() * 8) && BigEndianTarget) {
333348
Buffer.pushData(Buff.data() + (Buff.size() - 1 - (BitWidth / 8)),
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
// RUN: %clang_cc1 -verify=ref,both -std=c++2a -fsyntax-only -triple x86_64-apple-macosx10.14.0 %s
2+
// RUN: %clang_cc1 -verify=ref,both -std=c++2a -fsyntax-only -triple x86_64-apple-macosx10.14.0 %s -fno-signed-char
3+
// RUN: %clang_cc1 -verify=ref,both -std=c++2a -fsyntax-only -triple aarch64_be-linux-gnu %s
4+
// RUN: %clang_cc1 -verify=ref,both -std=c++2a -fsyntax-only %s
5+
6+
// RUN: %clang_cc1 -verify=expected,both -std=c++2a -fsyntax-only -triple x86_64-apple-macosx10.14.0 %s -fexperimental-new-constant-interpreter
7+
// RUN: %clang_cc1 -verify=expected,both -std=c++2a -fsyntax-only -triple x86_64-apple-macosx10.14.0 %s -fno-signed-char -fexperimental-new-constant-interpreter
8+
// RUN: %clang_cc1 -verify=expected,both -std=c++2a -fsyntax-only -triple aarch64_be-linux-gnu %s -fexperimental-new-constant-interpreter
9+
// RUN: %clang_cc1 -verify=expected,both -std=c++2a -fsyntax-only %s -fexperimental-new-constant-interpreter
10+
11+
// both-no-diagnostics
12+
13+
typedef decltype(nullptr) nullptr_t;
14+
typedef __INTPTR_TYPE__ intptr_t;
15+
16+
static_assert(sizeof(int) == 4);
17+
static_assert(sizeof(long long) == 8);
18+
19+
template <class To, class From>
20+
constexpr To bit_cast(const From &from) {
21+
static_assert(sizeof(To) == sizeof(From));
22+
return __builtin_bit_cast(To, from);
23+
}
24+
25+
template <class Intermediate, class Init>
26+
constexpr bool check_round_trip(const Init &init) {
27+
return bit_cast<Init>(bit_cast<Intermediate>(init)) == init;
28+
}
29+
30+
template <class Intermediate, class Init>
31+
constexpr Init round_trip(const Init &init) {
32+
return bit_cast<Init>(bit_cast<Intermediate>(init));
33+
}
34+
35+
36+
37+
38+
namespace test_long_double {
39+
#if __x86_64
40+
#if 0
41+
constexpr __int128_t test_cast_to_int128 = bit_cast<__int128_t>((long double)0); // expected-error{{must be initialized by a constant expression}}\
42+
// expected-note{{in call}}
43+
#endif
44+
constexpr long double ld = 3.1425926539;
45+
46+
struct bytes {
47+
unsigned char d[16];
48+
};
49+
50+
// static_assert(round_trip<bytes>(ld), "");
51+
52+
static_assert(round_trip<long double>(10.0L));
53+
54+
#if 0
55+
constexpr bool f(bool read_uninit) {
56+
bytes b = bit_cast<bytes>(ld);
57+
unsigned char ld_bytes[10] = {
58+
0x0, 0x48, 0x9f, 0x49, 0xf0,
59+
0x3c, 0x20, 0xc9, 0x0, 0x40,
60+
};
61+
62+
for (int i = 0; i != 10; ++i)
63+
if (ld_bytes[i] != b.d[i])
64+
return false;
65+
66+
if (read_uninit && b.d[10]) // expected-note{{read of uninitialized object is not allowed in a constant expression}}
67+
return false;
68+
69+
return true;
70+
}
71+
72+
static_assert(f(/*read_uninit=*/false), "");
73+
static_assert(f(/*read_uninit=*/true), ""); // expected-error{{static assertion expression is not an integral constant expression}} \
74+
// expected-note{{in call to 'f(true)'}}
75+
#endif
76+
constexpr bytes ld539 = {
77+
0x0, 0x0, 0x0, 0x0,
78+
0x0, 0x0, 0xc0, 0x86,
79+
0x8, 0x40, 0x0, 0x0,
80+
0x0, 0x0, 0x0, 0x0,
81+
};
82+
83+
constexpr long double fivehundredandthirtynine = 539.0;
84+
85+
static_assert(bit_cast<long double>(ld539) == fivehundredandthirtynine, "");
86+
#else
87+
static_assert(round_trip<__int128_t>(34.0L));
88+
#endif
89+
}

clang/test/AST/ByteCode/builtin-bit-cast.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@ static_assert(__builtin_bit_cast(__int128_t, OneBit) == (LITTLE_END ? 1 : Expect
8787

8888
#endif
8989

90+
static_assert(check_round_trip<double>(17.0));
91+
9092

9193
namespace simple {
9294
constexpr int A = __builtin_bit_cast(int, 10);
@@ -119,7 +121,7 @@ namespace simple {
119121
static_assert(check_round_trip<unsigned>((int)0x12345678));
120122
static_assert(check_round_trip<unsigned>((int)0x87654321));
121123
static_assert(check_round_trip<unsigned>((int)0x0C05FEFE));
122-
// static_assert(round_trip<float>((int)0x0C05FEFE));
124+
static_assert(round_trip<float>((int)0x0C05FEFE));
123125

124126

125127
/// This works in GCC and in the bytecode interpreter, but the current interpreter

0 commit comments

Comments
 (0)