diff --git a/clang/lib/AST/ByteCode/BitcastBuffer.h b/clang/lib/AST/ByteCode/BitcastBuffer.h index 2a0d8a0cd9a81..b1b6b9e5173a7 100644 --- a/clang/lib/AST/ByteCode/BitcastBuffer.h +++ b/clang/lib/AST/ByteCode/BitcastBuffer.h @@ -45,6 +45,7 @@ struct Bits { bool operator>=(Bits Other) const { return N >= Other.N; } bool operator<=(Bits Other) const { return N <= Other.N; } bool operator==(Bits Other) const { return N == Other.N; } + bool operator!=(Bits Other) const { return N != Other.N; } }; /// A quantity in bytes. diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h index c5c2a5ef19cc4..cdf05e36304ac 100644 --- a/clang/lib/AST/ByteCode/Interp.h +++ b/clang/lib/AST/ByteCode/Interp.h @@ -14,6 +14,7 @@ #define LLVM_CLANG_AST_INTERP_INTERP_H #include "../ExprConstShared.h" +#include "BitcastBuffer.h" #include "Boolean.h" #include "DynamicAllocator.h" #include "FixedPoint.h" @@ -3050,7 +3051,16 @@ inline bool BitCast(InterpState &S, CodePtr OpPC, bool TargetIsUCharOrByte, llvm::SmallVector Buff(BuffSize); bool HasIndeterminateBits = false; - if (!DoBitCast(S, OpPC, FromPtr, Buff.data(), BuffSize, HasIndeterminateBits)) + Bits FullBitWidth(ResultBitWidth); + Bits BitWidth = FullBitWidth; + + if constexpr (std::is_same_v) { + assert(Sem); + BitWidth = Bits(llvm::APFloatBase::getSizeInBits(*Sem)); + } + + if (!DoBitCast(S, OpPC, FromPtr, Buff.data(), BitWidth, FullBitWidth, + HasIndeterminateBits)) return false; if (!CheckBitCast(S, OpPC, HasIndeterminateBits, TargetIsUCharOrByte)) @@ -3058,16 +3068,7 @@ inline bool BitCast(InterpState &S, CodePtr OpPC, bool TargetIsUCharOrByte, if constexpr (std::is_same_v) { assert(Sem); - ptrdiff_t Offset = 0; - - if (llvm::sys::IsBigEndianHost) { - unsigned NumBits = llvm::APFloatBase::getSizeInBits(*Sem); - assert(NumBits % 8 == 0); - assert(NumBits <= ResultBitWidth); - Offset = (ResultBitWidth - NumBits) / 8; - } - - S.Stk.push(T::bitcastFromMemory(Buff.data() + Offset, *Sem)); + S.Stk.push(T::bitcastFromMemory(Buff.data(), *Sem)); } else { assert(!Sem); S.Stk.push(T::bitcastFromMemory(Buff.data(), ResultBitWidth)); diff --git a/clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp b/clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp index e12babc162ce3..c9cd113287557 100644 --- a/clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp +++ b/clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp @@ -278,44 +278,49 @@ static bool readPointerToBuffer(const Context &Ctx, const Pointer &FromPtr, if (llvm::sys::IsBigEndianHost) swapBytes(Buff.get(), NumBits.roundToBytes()); + Buffer.markInitialized(BitOffset, NumBits); } else { BITCAST_TYPE_SWITCH(T, { P.deref().bitcastToMemory(Buff.get()); }); if (llvm::sys::IsBigEndianHost) swapBytes(Buff.get(), FullBitWidth.roundToBytes()); + Buffer.markInitialized(BitOffset, BitWidth); } Buffer.pushData(Buff.get(), BitOffset, BitWidth, TargetEndianness); - Buffer.markInitialized(BitOffset, BitWidth); return true; }); } bool clang::interp::DoBitCast(InterpState &S, CodePtr OpPC, const Pointer &Ptr, - std::byte *Buff, size_t BuffSize, + std::byte *Buff, Bits BitWidth, Bits FullBitWidth, bool &HasIndeterminateBits) { assert(Ptr.isLive()); assert(Ptr.isBlockPointer()); assert(Buff); + assert(BitWidth <= FullBitWidth); + assert(FullBitWidth.isFullByte()); + assert(BitWidth.isFullByte()); - Bits BitSize = Bytes(BuffSize).toBits(); - BitcastBuffer Buffer(BitSize); + BitcastBuffer Buffer(FullBitWidth); + size_t BuffSize = FullBitWidth.roundToBytes(); if (!CheckBitcastType(S, OpPC, Ptr.getType(), /*IsToType=*/false)) return false; bool Success = readPointerToBuffer(S.getContext(), Ptr, Buffer, /*ReturnOnUninit=*/false); - HasIndeterminateBits = !Buffer.allInitialized(); + HasIndeterminateBits = !Buffer.rangeInitialized(Bits::zero(), BitWidth); const ASTContext &ASTCtx = S.getASTContext(); Endian TargetEndianness = ASTCtx.getTargetInfo().isLittleEndian() ? Endian::Little : Endian::Big; - auto B = Buffer.copyBits(Bits::zero(), BitSize, BitSize, TargetEndianness); + auto B = + Buffer.copyBits(Bits::zero(), BitWidth, FullBitWidth, TargetEndianness); std::memcpy(Buff, B.get(), BuffSize); if (llvm::sys::IsBigEndianHost) - swapBytes(Buff, BuffSize); + swapBytes(Buff, BitWidth.roundToBytes()); return Success; } diff --git a/clang/lib/AST/ByteCode/InterpBuiltinBitCast.h b/clang/lib/AST/ByteCode/InterpBuiltinBitCast.h index 8142e0bf28fcf..92e6ffc79fc4f 100644 --- a/clang/lib/AST/ByteCode/InterpBuiltinBitCast.h +++ b/clang/lib/AST/ByteCode/InterpBuiltinBitCast.h @@ -9,6 +9,7 @@ #ifndef LLVM_CLANG_AST_INTERP_BUILITN_BIT_CAST_H #define LLVM_CLANG_AST_INTERP_BUILITN_BIT_CAST_H +#include "BitcastBuffer.h" #include namespace clang { @@ -18,7 +19,8 @@ class InterpState; class CodePtr; bool DoBitCast(InterpState &S, CodePtr OpPC, const Pointer &Ptr, - std::byte *Buff, size_t BuffSize, bool &HasIndeterminateBits); + std::byte *Buff, Bits BitWidth, Bits FullBitWidth, + bool &HasIndeterminateBits); bool DoBitCastPtr(InterpState &S, CodePtr OpPC, const Pointer &FromPtr, Pointer &ToPtr); bool DoBitCastPtr(InterpState &S, CodePtr OpPC, const Pointer &FromPtr, diff --git a/clang/test/AST/ByteCode/builtin-bit-cast-long-double.cpp b/clang/test/AST/ByteCode/builtin-bit-cast-long-double.cpp index 0929f7cb70b74..710612bef8fd0 100644 --- a/clang/test/AST/ByteCode/builtin-bit-cast-long-double.cpp +++ b/clang/test/AST/ByteCode/builtin-bit-cast-long-double.cpp @@ -6,7 +6,10 @@ // 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 // RUN: %clang_cc1 -verify=expected,both -std=c++2a -fsyntax-only -triple aarch64_be-linux-gnu %s -fexperimental-new-constant-interpreter +#if !__x86_64 // both-no-diagnostics +#endif + typedef decltype(nullptr) nullptr_t; typedef __INTPTR_TYPE__ intptr_t; @@ -35,6 +38,7 @@ constexpr Init round_trip(const Init &init) { namespace test_long_double { #if __x86_64 +/// FIXME: We could enable this, but since it aborts, it causes the usual mempory leak. #if 0 constexpr __int128_t test_cast_to_int128 = bit_cast<__int128_t>((long double)0); // expected-error{{must be initialized by a constant expression}}\ // expected-note{{in call}} @@ -49,7 +53,13 @@ static_assert(round_trip(ld), ""); static_assert(round_trip(10.0L)); -#if 0 +constexpr long double foo() { + bytes A = __builtin_bit_cast(bytes, ld); + long double ld = __builtin_bit_cast(long double, A); + return ld; +} +static_assert(foo() == ld); + constexpr bool f(bool read_uninit) { bytes b = bit_cast(ld); unsigned char ld_bytes[10] = { @@ -61,16 +71,15 @@ constexpr bool f(bool read_uninit) { if (ld_bytes[i] != b.d[i]) return false; - if (read_uninit && b.d[10]) // expected-note{{read of uninitialized object is not allowed in a constant expression}} + if (read_uninit && b.d[10]) // both-note{{read of uninitialized object is not allowed in a constant expression}} return false; return true; } static_assert(f(/*read_uninit=*/false), ""); -static_assert(f(/*read_uninit=*/true), ""); // expected-error{{static assertion expression is not an integral constant expression}} \ - // expected-note{{in call to 'f(true)'}} -#endif +static_assert(f(/*read_uninit=*/true), ""); // both-error{{static assertion expression is not an integral constant expression}} \ + // both-note{{in call to 'f(true)'}} constexpr bytes ld539 = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc0, 0x86,