Skip to content

Commit 662447b

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 662447b

File tree

4 files changed

+128
-14
lines changed

4 files changed

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

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)