Skip to content

[clang][bytecode] Handle bitcasts involving bitfields #116843

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Dec 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 95 additions & 0 deletions clang/lib/AST/ByteCode/BitcastBuffer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
//===-------------------- Bitcastbuffer.cpp ---------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "BitcastBuffer.h"

using namespace clang;
using namespace clang::interp;

/// Returns the value of the bit in the given sequence of bytes.
static inline bool bitof(const std::byte *B, Bits BitIndex) {
return (B[BitIndex.roundToBytes()] &
(std::byte{1} << BitIndex.getOffsetInByte())) != std::byte{0};
}

void BitcastBuffer::pushData(const std::byte *In, Bits BitOffset, Bits BitWidth,
Endian TargetEndianness) {
for (unsigned It = 0; It != BitWidth.getQuantity(); ++It) {
bool BitValue = bitof(In, Bits(It));
if (!BitValue)
continue;

Bits DstBit;
if (TargetEndianness == Endian::Little)
DstBit = BitOffset + Bits(It);
else
DstBit = size() - BitOffset - BitWidth + Bits(It);

size_t DstByte = DstBit.roundToBytes();
Data[DstByte] |= std::byte{1} << DstBit.getOffsetInByte();
}
}

std::unique_ptr<std::byte[]>
BitcastBuffer::copyBits(Bits BitOffset, Bits BitWidth, Bits FullBitWidth,
Endian TargetEndianness) const {
assert(BitWidth.getQuantity() <= FullBitWidth.getQuantity());
assert(FullBitWidth.isFullByte());
auto Out = std::make_unique<std::byte[]>(FullBitWidth.roundToBytes());

for (unsigned It = 0; It != BitWidth.getQuantity(); ++It) {
Bits BitIndex;
if (TargetEndianness == Endian::Little)
BitIndex = BitOffset + Bits(It);
else
BitIndex = size() - BitWidth - BitOffset + Bits(It);

bool BitValue = bitof(Data.get(), BitIndex);
if (!BitValue)
continue;

Bits DstBit = Bits(It);
size_t DstByte = DstBit.roundToBytes();
Out[DstByte] |= std::byte{1} << DstBit.getOffsetInByte();
}

return Out;
}

#if 0
template<typename T>
static std::string hex(T t) {
std::stringstream stream;
stream << std::hex << (int)t;
return std::string(stream.str());
}


void BitcastBuffer::dump(bool AsHex = true) const {
llvm::errs() << "LSB\n ";
unsigned LineLength = 0;
for (unsigned I = 0; I != (FinalBitSize / 8); ++I) {
std::byte B = Data[I];
if (AsHex) {
std::stringstream stream;
stream << std::hex << (int)B;
llvm::errs() << stream.str();
LineLength += stream.str().size() + 1;
} else {
llvm::errs() << std::bitset<8>((int)B).to_string();
LineLength += 8 + 1;
// llvm::errs() << (int)B;
}
llvm::errs() << ' ';
}
llvm::errs() << '\n';

for (unsigned I = 0; I != LineLength; ++I)
llvm::errs() << ' ';
llvm::errs() << "MSB\n";
}
#endif
88 changes: 88 additions & 0 deletions clang/lib/AST/ByteCode/BitcastBuffer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
//===--------------------- BitcastBuffer.h ----------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_AST_INTERP_BITCAST_BUFFER_H
#define LLVM_CLANG_AST_INTERP_BITCAST_BUFFER_H

#include <cassert>
#include <cstddef>
#include <memory>

namespace clang {
namespace interp {

enum class Endian { Little, Big };

/// A quantity in bits.
struct Bits {
size_t N = 0;
Bits() = default;
static Bits zero() { return Bits(0); }
explicit Bits(size_t Quantity) : N(Quantity) {}
size_t getQuantity() const { return N; }
size_t roundToBytes() const { return N / 8; }
size_t getOffsetInByte() const { return N % 8; }
bool isFullByte() const { return N % 8 == 0; }
bool nonZero() const { return N != 0; }

Bits operator-(Bits Other) { return Bits(N - Other.N); }
Bits operator+(Bits Other) { return Bits(N + Other.N); }
Bits operator+=(size_t O) {
N += O;
return *this;
}

bool operator>=(Bits Other) { return N >= Other.N; }
};

/// A quantity in bytes.
struct Bytes {
size_t N;
explicit Bytes(size_t Quantity) : N(Quantity) {}
size_t getQuantity() const { return N; }
Bits toBits() const { return Bits(N * 8); }
};

/// Track what bits have been initialized to known values and which ones
/// have indeterminate value.
struct BitcastBuffer {
Bits FinalBitSize;
std::unique_ptr<std::byte[]> Data;

BitcastBuffer(Bits FinalBitSize) : FinalBitSize(FinalBitSize) {
assert(FinalBitSize.isFullByte());
unsigned ByteSize = FinalBitSize.roundToBytes();
Data = std::make_unique<std::byte[]>(ByteSize);
}

/// Returns the buffer size in bits.
Bits size() const { return FinalBitSize; }

/// Returns \c true if all bits in the buffer have been initialized.
bool allInitialized() const {
// FIXME: Implement.
return true;
}

/// Push \p BitWidth bits at \p BitOffset from \p In into the buffer.
/// \p TargetEndianness is the endianness of the target we're compiling for.
/// \p In must hold at least \p BitWidth many bits.
void pushData(const std::byte *In, Bits BitOffset, Bits BitWidth,
Endian TargetEndianness);

/// Copy \p BitWidth bits at offset \p BitOffset from the buffer.
/// \p TargetEndianness is the endianness of the target we're compiling for.
///
/// The returned output holds exactly (\p FullBitWidth / 8) bytes.
std::unique_ptr<std::byte[]> copyBits(Bits BitOffset, Bits BitWidth,
Bits FullBitWidth,
Endian TargetEndianness) const;
};

} // namespace interp
} // namespace clang
#endif
4 changes: 1 addition & 3 deletions clang/lib/AST/ByteCode/Boolean.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,7 @@ class Boolean final {
Boolean truncate(unsigned TruncBits) const { return *this; }

static Boolean bitcastFromMemory(const std::byte *Buff, unsigned BitWidth) {
// Boolean width is currently always 8 for all supported targets. If this
// changes we need to get the bool width from the target info.
assert(BitWidth == 8);
// Just load the first byte.
bool Val = static_cast<bool>(*Buff);
return Boolean(Val);
}
Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/ByteCode/Integral.h
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ template <unsigned Bits, bool Signed> class Integral final {
}

Integral truncate(unsigned TruncBits) const {
assert(TruncBits >= 1);
if (TruncBits >= Bits)
return *this;
const ReprT BitMask = (ReprT(1) << ReprT(TruncBits)) - 1;
Expand Down
Loading
Loading