Skip to content

Commit 81d236a

Browse files
committed
[clang][bytecode] Start implementing __builtin_bit_cast
1 parent 490b7d1 commit 81d236a

File tree

14 files changed

+929
-0
lines changed

14 files changed

+929
-0
lines changed

clang/lib/AST/ByteCode/Boolean.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,16 @@ class Boolean final {
8181

8282
Boolean truncate(unsigned TruncBits) const { return *this; }
8383

84+
static Boolean bitcastFromMemory(const std::byte *Buff, unsigned BitWidth) {
85+
// Boolean width is currently always 8 for all supported targets. If this
86+
// changes we need to get the bool width from the target info.
87+
assert(BitWidth == 8);
88+
bool Val = static_cast<bool>(*Buff);
89+
return Boolean(Val);
90+
}
91+
92+
void bitcastToMemory(std::byte *Buff) { std::memcpy(Buff, &V, sizeof(V)); }
93+
8494
void print(llvm::raw_ostream &OS) const { OS << (V ? "true" : "false"); }
8595
std::string toDiagnosticString(const ASTContext &Ctx) const {
8696
std::string NameStr;

clang/lib/AST/ByteCode/Compiler.cpp

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,9 @@ bool Compiler<Emitter>::VisitCastExpr(const CastExpr *CE) {
470470
return this->emitDecayPtr(*FromT, *ToT, CE);
471471
}
472472

473+
case CK_LValueToRValueBitCast:
474+
return this->emitBuiltinBitCast(CE);
475+
473476
case CK_IntegralToBoolean:
474477
case CK_FixedPointToBoolean:
475478
case CK_BooleanToSignedIntegral:
@@ -6419,6 +6422,67 @@ bool Compiler<Emitter>::emitDestruction(const Descriptor *Desc,
64196422
return this->emitRecordDestruction(Desc->ElemRecord, Loc);
64206423
}
64216424

6425+
// This function is constexpr if and only if To, From, and the types of
6426+
// all subobjects of To and From are types T such that...
6427+
// (3.1) - is_union_v<T> is false;
6428+
// (3.2) - is_pointer_v<T> is false;
6429+
// (3.3) - is_member_pointer_v<T> is false;
6430+
// (3.4) - is_volatile_v<T> is false; and
6431+
// (3.5) - T has no non-static data members of reference type
6432+
template <class Emitter>
6433+
bool Compiler<Emitter>::emitBuiltinBitCast(const CastExpr *E) {
6434+
const Expr *SubExpr = E->getSubExpr();
6435+
QualType FromType = SubExpr->getType();
6436+
QualType ToType = E->getType();
6437+
std::optional<PrimType> ToT = classify(ToType);
6438+
6439+
assert(!DiscardResult && "Implement DiscardResult mode for bitcasts.");
6440+
6441+
if (ToType->isNullPtrType()) {
6442+
if (!this->discard(SubExpr))
6443+
return false;
6444+
6445+
return this->emitNullPtr(nullptr, E);
6446+
}
6447+
6448+
if (FromType->isNullPtrType() && ToT) {
6449+
if (!this->discard(SubExpr))
6450+
return false;
6451+
6452+
return visitZeroInitializer(*ToT, ToType, E);
6453+
}
6454+
assert(!ToType->isReferenceType());
6455+
6456+
// Get a pointer to the value-to-cast on the stack.
6457+
if (!this->visit(SubExpr))
6458+
return false;
6459+
6460+
if (!ToT || ToT == PT_Ptr) {
6461+
// Conversion to an array or record type.
6462+
assert(false && "Implement bitcast to pointers.");
6463+
}
6464+
assert(ToT);
6465+
6466+
const llvm::fltSemantics *TargetSemantics = nullptr;
6467+
if (ToT == PT_Float)
6468+
TargetSemantics = &Ctx.getFloatSemantics(ToType);
6469+
6470+
// Conversion to a primitive type. FromType can be another
6471+
// primitive type, or a record/array.
6472+
bool ToTypeIsUChar = (ToType->isSpecificBuiltinType(BuiltinType::UChar) ||
6473+
ToType->isSpecificBuiltinType(BuiltinType::Char_U));
6474+
uint32_t ResultBitWidth = std::max(Ctx.getBitWidth(ToType), 8u);
6475+
6476+
if (!this->emitBitCast(*ToT, ToTypeIsUChar || ToType->isStdByteType(),
6477+
ResultBitWidth, TargetSemantics, E))
6478+
return false;
6479+
6480+
if (DiscardResult)
6481+
return this->emitPop(*ToT, E);
6482+
6483+
return true;
6484+
}
6485+
64226486
namespace clang {
64236487
namespace interp {
64246488

clang/lib/AST/ByteCode/Compiler.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,7 @@ class Compiler : public ConstStmtVisitor<Compiler<Emitter>, bool>,
373373
unsigned collectBaseOffset(const QualType BaseType,
374374
const QualType DerivedType);
375375
bool emitLambdaStaticInvokerBody(const CXXMethodDecl *MD);
376+
bool emitBuiltinBitCast(const CastExpr *E);
376377
bool compileConstructor(const CXXConstructorDecl *Ctor);
377378
bool compileDestructor(const CXXDestructorDecl *Dtor);
378379

clang/lib/AST/ByteCode/Floating.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,11 @@ class Floating final {
135135
return Floating(APFloat(Sem, API));
136136
}
137137

138+
void bitcastToMemory(std::byte *Buff) {
139+
llvm::APInt API = F.bitcastToAPInt();
140+
llvm::StoreIntToMemory(API, (uint8_t *)Buff, bitWidth() / 8);
141+
}
142+
138143
// === Serialization support ===
139144
size_t bytesToSerialize() const {
140145
return sizeof(llvm::fltSemantics *) +

clang/lib/AST/ByteCode/Integral.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,18 @@ template <unsigned Bits, bool Signed> class Integral final {
154154
return Compare(V, RHS.V);
155155
}
156156

157+
void bitcastToMemory(std::byte *Dest) const {
158+
std::memcpy(Dest, &V, sizeof(V));
159+
}
160+
161+
static Integral bitcastFromMemory(const std::byte *Src, unsigned BitWidth) {
162+
assert(BitWidth == sizeof(ReprT) * 8);
163+
ReprT V;
164+
165+
std::memcpy(&V, Src, sizeof(ReprT));
166+
return Integral(V);
167+
}
168+
157169
std::string toDiagnosticString(const ASTContext &Ctx) const {
158170
std::string NameStr;
159171
llvm::raw_string_ostream OS(NameStr);

clang/lib/AST/ByteCode/IntegralAP.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,12 @@ template <bool Signed> class IntegralAP final {
171171
return IntegralAP<false>(Copy);
172172
}
173173

174+
void bitcastToMemory(std::byte *Dest) const { assert(false); }
175+
176+
static IntegralAP bitcastFromMemory(const std::byte *Src, unsigned BitWidth) {
177+
return IntegralAP();
178+
}
179+
174180
ComparisonCategoryResult compare(const IntegralAP &RHS) const {
175181
assert(Signed == RHS.isSigned());
176182
assert(bitWidth() == RHS.bitWidth());

clang/lib/AST/ByteCode/Interp.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1545,6 +1545,23 @@ bool CastPointerIntegralAPS(InterpState &S, CodePtr OpPC, uint32_t BitWidth) {
15451545
return true;
15461546
}
15471547

1548+
bool CheckBitCast(InterpState &S, CodePtr OpPC, bool HasIndeterminateBits,
1549+
bool TargetIsUCharOrByte) {
1550+
// This is always fine.
1551+
if (!HasIndeterminateBits)
1552+
return true;
1553+
1554+
// Indeterminate bits can only be bitcast to unsigned char or std::byte.
1555+
if (TargetIsUCharOrByte)
1556+
return true;
1557+
1558+
const Expr *E = S.Current->getExpr(OpPC);
1559+
QualType ExprType = E->getType();
1560+
S.FFDiag(E, diag::note_constexpr_bit_cast_indet_dest)
1561+
<< ExprType << S.getLangOpts().CharIsSigned << E->getSourceRange();
1562+
return false;
1563+
}
1564+
15481565
// https://github.com/llvm/llvm-project/issues/102513
15491566
#if defined(_WIN32) && !defined(__clang__) && !defined(NDEBUG)
15501567
#pragma optimize("", off)

clang/lib/AST/ByteCode/Interp.h

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "Floating.h"
2121
#include "Function.h"
2222
#include "FunctionPointer.h"
23+
#include "InterpBuiltinBitCast.h"
2324
#include "InterpFrame.h"
2425
#include "InterpStack.h"
2526
#include "InterpState.h"
@@ -162,6 +163,8 @@ bool CallPtr(InterpState &S, CodePtr OpPC, uint32_t ArgSize,
162163
const CallExpr *CE);
163164
bool CheckLiteralType(InterpState &S, CodePtr OpPC, const Type *T);
164165
bool InvalidShuffleVectorIndex(InterpState &S, CodePtr OpPC, uint32_t Index);
166+
bool CheckBitCast(InterpState &S, CodePtr OpPC, bool HasIndeterminateBits,
167+
bool TargetIsUCharOrByte);
165168

166169
template <typename T>
167170
static bool handleOverflow(InterpState &S, CodePtr OpPC, const T &SrcValue) {
@@ -3022,6 +3025,34 @@ bool CheckNewTypeMismatchArray(InterpState &S, CodePtr OpPC, const Expr *E) {
30223025
return CheckNewTypeMismatch(S, OpPC, E, static_cast<uint64_t>(Size));
30233026
}
30243027
bool InvalidNewDeleteExpr(InterpState &S, CodePtr OpPC, const Expr *E);
3028+
3029+
template <PrimType Name, class T = typename PrimConv<Name>::T>
3030+
inline bool BitCast(InterpState &S, CodePtr OpPC, bool TargetIsUCharOrByte,
3031+
uint32_t ResultBitWidth, const llvm::fltSemantics *Sem) {
3032+
const Pointer &FromPtr = S.Stk.pop<Pointer>();
3033+
3034+
if (!CheckLoad(S, OpPC, FromPtr))
3035+
return false;
3036+
3037+
size_t BuffSize = ResultBitWidth / 8;
3038+
llvm::SmallVector<std::byte> Buff(BuffSize);
3039+
bool HasIndeterminateBits = false;
3040+
3041+
if (!DoBitCast(S, OpPC, FromPtr, Buff.data(), BuffSize, HasIndeterminateBits))
3042+
return false;
3043+
3044+
if (!CheckBitCast(S, OpPC, HasIndeterminateBits, TargetIsUCharOrByte))
3045+
return false;
3046+
3047+
if constexpr (std::is_same_v<T, Floating>) {
3048+
assert(false && "Implement bitcasting to a floating type");
3049+
} else {
3050+
assert(!Sem);
3051+
S.Stk.push<T>(T::bitcastFromMemory(Buff.data(), ResultBitWidth));
3052+
}
3053+
return true;
3054+
}
3055+
30253056
//===----------------------------------------------------------------------===//
30263057
// Read opcode arguments
30273058
//===----------------------------------------------------------------------===//

clang/lib/AST/ByteCode/InterpBuiltin.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "Compiler.h"
1111
#include "EvalEmitter.h"
1212
#include "Interp.h"
13+
#include "InterpBuiltinBitCast.h"
1314
#include "PrimType.h"
1415
#include "clang/AST/OSLog.h"
1516
#include "clang/AST/RecordLayout.h"

0 commit comments

Comments
 (0)