From 3f041d0338c51d3c279f004220b575c913dd1442 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timm=20B=C3=A4der?= Date: Sun, 8 Dec 2024 08:00:14 +0100 Subject: [PATCH] [clang][bytecode] Pass (float) BitWidth to DoBitCast In certain cases (i.e. long double on x86), the bit with we get from the floating point semantics is different than the type size we compute for the BitCast instruction. Pass this along to DoBitCast, so in there we can check only the relevant bits for being initialized. This also fixes a weirdness we still had in DoBitCast. --- clang/lib/AST/ByteCode/BitcastBuffer.h | 1 + clang/lib/AST/ByteCode/Interp.h | 23 ++++++++++--------- .../lib/AST/ByteCode/InterpBuiltinBitCast.cpp | 19 +++++++++------ clang/lib/AST/ByteCode/InterpBuiltinBitCast.h | 4 +++- .../ByteCode/builtin-bit-cast-long-double.cpp | 19 +++++++++++---- 5 files changed, 42 insertions(+), 24 deletions(-) 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,