Skip to content

Commit

Permalink
[clang][bytecode] Start implementing __builtin_bit_cast
Browse files Browse the repository at this point in the history
  • Loading branch information
tbaederr committed Oct 30, 2024
1 parent 88e23eb commit 3e02839
Show file tree
Hide file tree
Showing 14 changed files with 928 additions and 0 deletions.
10 changes: 10 additions & 0 deletions clang/lib/AST/ByteCode/Boolean.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,16 @@ 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);
bool Val = static_cast<bool>(*Buff);
return Boolean(Val);
}

void bitcastToMemory(std::byte *Buff) { std::memcpy(Buff, &V, sizeof(V)); }

void print(llvm::raw_ostream &OS) const { OS << (V ? "true" : "false"); }
std::string toDiagnosticString(const ASTContext &Ctx) const {
std::string NameStr;
Expand Down
63 changes: 63 additions & 0 deletions clang/lib/AST/ByteCode/Compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -470,6 +470,9 @@ bool Compiler<Emitter>::VisitCastExpr(const CastExpr *CE) {
return this->emitDecayPtr(*FromT, *ToT, CE);
}

case CK_LValueToRValueBitCast:
return this->emitBuiltinBitCast(CE);

case CK_IntegralToBoolean:
case CK_FixedPointToBoolean:
case CK_BooleanToSignedIntegral:
Expand Down Expand Up @@ -6426,6 +6429,66 @@ bool Compiler<Emitter>::emitDummyPtr(const DeclTy &D, const Expr *E) {
return this->emitDecayPtr(PT_Ptr, PT, E);
return false;
}
return true;
}

// This function is constexpr if and only if To, From, and the types of
// all subobjects of To and From are types T such that...
// (3.1) - is_union_v<T> is false;
// (3.2) - is_pointer_v<T> is false;
// (3.3) - is_member_pointer_v<T> is false;
// (3.4) - is_volatile_v<T> is false; and
// (3.5) - T has no non-static data members of reference type
template <class Emitter>
bool Compiler<Emitter>::emitBuiltinBitCast(const CastExpr *E) {
const Expr *SubExpr = E->getSubExpr();
QualType FromType = SubExpr->getType();
QualType ToType = E->getType();
std::optional<PrimType> ToT = classify(ToType);

assert(!DiscardResult && "Implement DiscardResult mode for bitcasts.");

if (ToType->isNullPtrType()) {
if (!this->discard(SubExpr))
return false;

return this->emitNullPtr(nullptr, E);
}

if (FromType->isNullPtrType() && ToT) {
if (!this->discard(SubExpr))
return false;

return visitZeroInitializer(*ToT, ToType, E);
}
assert(!ToType->isReferenceType());

// Get a pointer to the value-to-cast on the stack.
if (!this->visit(SubExpr))
return false;

if (!ToT || ToT == PT_Ptr) {
// Conversion to an array or record type.
assert(false && "Implement bitcast to pointers.");
}
assert(ToT);

const llvm::fltSemantics *TargetSemantics = nullptr;
if (ToT == PT_Float)
TargetSemantics = &Ctx.getFloatSemantics(ToType);

// Conversion to a primitive type. FromType can be another
// primitive type, or a record/array.
bool ToTypeIsUChar = (ToType->isSpecificBuiltinType(BuiltinType::UChar) ||
ToType->isSpecificBuiltinType(BuiltinType::Char_U));
uint32_t ResultBitWidth = std::max(Ctx.getBitWidth(ToType), 8u);

if (!this->emitBitCast(*ToT, ToTypeIsUChar || ToType->isStdByteType(),
ResultBitWidth, TargetSemantics, E))
return false;

if (DiscardResult)
return this->emitPop(*ToT, E);

return true;
}
Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/ByteCode/Compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,7 @@ class Compiler : public ConstStmtVisitor<Compiler<Emitter>, bool>,
unsigned collectBaseOffset(const QualType BaseType,
const QualType DerivedType);
bool emitLambdaStaticInvokerBody(const CXXMethodDecl *MD);
bool emitBuiltinBitCast(const CastExpr *E);
bool compileConstructor(const CXXConstructorDecl *Ctor);
bool compileDestructor(const CXXDestructorDecl *Dtor);

Expand Down
5 changes: 5 additions & 0 deletions clang/lib/AST/ByteCode/Floating.h
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,11 @@ class Floating final {
return Floating(APFloat(Sem, API));
}

void bitcastToMemory(std::byte *Buff) {
llvm::APInt API = F.bitcastToAPInt();
llvm::StoreIntToMemory(API, (uint8_t *)Buff, bitWidth() / 8);
}

// === Serialization support ===
size_t bytesToSerialize() const {
return sizeof(llvm::fltSemantics *) +
Expand Down
12 changes: 12 additions & 0 deletions clang/lib/AST/ByteCode/Integral.h
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,18 @@ template <unsigned Bits, bool Signed> class Integral final {
return Compare(V, RHS.V);
}

void bitcastToMemory(std::byte *Dest) const {
std::memcpy(Dest, &V, sizeof(V));
}

static Integral bitcastFromMemory(const std::byte *Src, unsigned BitWidth) {
assert(BitWidth == sizeof(ReprT) * 8);
ReprT V;

std::memcpy(&V, Src, sizeof(ReprT));
return Integral(V);
}

std::string toDiagnosticString(const ASTContext &Ctx) const {
std::string NameStr;
llvm::raw_string_ostream OS(NameStr);
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/AST/ByteCode/IntegralAP.h
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,12 @@ template <bool Signed> class IntegralAP final {
return IntegralAP<false>(Copy);
}

void bitcastToMemory(std::byte *Dest) const { assert(false); }

static IntegralAP bitcastFromMemory(const std::byte *Src, unsigned BitWidth) {
return IntegralAP();
}

ComparisonCategoryResult compare(const IntegralAP &RHS) const {
assert(Signed == RHS.isSigned());
assert(bitWidth() == RHS.bitWidth());
Expand Down
17 changes: 17 additions & 0 deletions clang/lib/AST/ByteCode/Interp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1551,6 +1551,23 @@ bool CastPointerIntegralAPS(InterpState &S, CodePtr OpPC, uint32_t BitWidth) {
return true;
}

bool CheckBitCast(InterpState &S, CodePtr OpPC, bool HasIndeterminateBits,
bool TargetIsUCharOrByte) {
// This is always fine.
if (!HasIndeterminateBits)
return true;

// Indeterminate bits can only be bitcast to unsigned char or std::byte.
if (TargetIsUCharOrByte)
return true;

const Expr *E = S.Current->getExpr(OpPC);
QualType ExprType = E->getType();
S.FFDiag(E, diag::note_constexpr_bit_cast_indet_dest)
<< ExprType << S.getLangOpts().CharIsSigned << E->getSourceRange();
return false;
}

// https://github.com/llvm/llvm-project/issues/102513
#if defined(_WIN32) && !defined(__clang__) && !defined(NDEBUG)
#pragma optimize("", off)
Expand Down
31 changes: 31 additions & 0 deletions clang/lib/AST/ByteCode/Interp.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "Floating.h"
#include "Function.h"
#include "FunctionPointer.h"
#include "InterpBuiltinBitCast.h"
#include "InterpFrame.h"
#include "InterpStack.h"
#include "InterpState.h"
Expand Down Expand Up @@ -162,6 +163,8 @@ bool CallPtr(InterpState &S, CodePtr OpPC, uint32_t ArgSize,
const CallExpr *CE);
bool CheckLiteralType(InterpState &S, CodePtr OpPC, const Type *T);
bool InvalidShuffleVectorIndex(InterpState &S, CodePtr OpPC, uint32_t Index);
bool CheckBitCast(InterpState &S, CodePtr OpPC, bool HasIndeterminateBits,
bool TargetIsUCharOrByte);

template <typename T>
static bool handleOverflow(InterpState &S, CodePtr OpPC, const T &SrcValue) {
Expand Down Expand Up @@ -3033,6 +3036,34 @@ bool CheckNewTypeMismatchArray(InterpState &S, CodePtr OpPC, const Expr *E) {
return CheckNewTypeMismatch(S, OpPC, E, static_cast<uint64_t>(Size));
}
bool InvalidNewDeleteExpr(InterpState &S, CodePtr OpPC, const Expr *E);

template <PrimType Name, class T = typename PrimConv<Name>::T>
inline bool BitCast(InterpState &S, CodePtr OpPC, bool TargetIsUCharOrByte,
uint32_t ResultBitWidth, const llvm::fltSemantics *Sem) {
const Pointer &FromPtr = S.Stk.pop<Pointer>();

if (!CheckLoad(S, OpPC, FromPtr))
return false;

size_t BuffSize = ResultBitWidth / 8;
llvm::SmallVector<std::byte> Buff(BuffSize);
bool HasIndeterminateBits = false;

if (!DoBitCast(S, OpPC, FromPtr, Buff.data(), BuffSize, HasIndeterminateBits))
return false;

if (!CheckBitCast(S, OpPC, HasIndeterminateBits, TargetIsUCharOrByte))
return false;

if constexpr (std::is_same_v<T, Floating>) {
assert(false && "Implement bitcasting to a floating type");
} else {
assert(!Sem);
S.Stk.push<T>(T::bitcastFromMemory(Buff.data(), ResultBitWidth));
}
return true;
}

//===----------------------------------------------------------------------===//
// Read opcode arguments
//===----------------------------------------------------------------------===//
Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/ByteCode/InterpBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "Compiler.h"
#include "EvalEmitter.h"
#include "Interp.h"
#include "InterpBuiltinBitCast.h"
#include "PrimType.h"
#include "clang/AST/OSLog.h"
#include "clang/AST/RecordLayout.h"
Expand Down
Loading

0 comments on commit 3e02839

Please sign in to comment.