Skip to content

Commit 1cf8a3b

Browse files
committed
[clang][bytecode] Start implementing __builtin_bit_cast
1 parent 5a7b79c commit 1cf8a3b

File tree

14 files changed

+893
-0
lines changed

14 files changed

+893
-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:
@@ -6409,6 +6412,67 @@ bool Compiler<Emitter>::emitDestruction(const Descriptor *Desc,
64096412
return this->emitRecordDestruction(Desc->ElemRecord, Loc);
64106413
}
64116414

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

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
@@ -151,6 +151,18 @@ template <unsigned Bits, bool Signed> class Integral final {
151151
return Compare(V, RHS.V);
152152
}
153153

154+
void bitcastToMemory(std::byte *Dest) const {
155+
std::memcpy(Dest, &V, sizeof(V));
156+
}
157+
158+
static Integral bitcastFromMemory(const std::byte *Src, unsigned BitWidth) {
159+
assert(BitWidth == sizeof(ReprT) * 8);
160+
ReprT V;
161+
162+
std::memcpy(&V, Src, sizeof(ReprT));
163+
return Integral(V);
164+
}
165+
154166
std::string toDiagnosticString(const ASTContext &Ctx) const {
155167
std::string NameStr;
156168
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
@@ -173,6 +173,12 @@ template <bool Signed> class IntegralAP final {
173173
return IntegralAP<false>(Copy);
174174
}
175175

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

clang/lib/AST/ByteCode/Interp.cpp

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

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

clang/lib/AST/ByteCode/Interp.h

Lines changed: 28 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) {
@@ -2995,6 +2998,31 @@ bool CheckNewTypeMismatchArray(InterpState &S, CodePtr OpPC, const Expr *E) {
29952998
return CheckNewTypeMismatch(S, OpPC, E, static_cast<uint64_t>(Size));
29962999
}
29973000
bool InvalidNewDeleteExpr(InterpState &S, CodePtr OpPC, const Expr *E);
3001+
3002+
template <PrimType Name, class T = typename PrimConv<Name>::T>
3003+
inline bool BitCast(InterpState &S, CodePtr OpPC, bool TargetIsUCharOrByte,
3004+
uint32_t ResultBitWidth, const llvm::fltSemantics *Sem) {
3005+
const Pointer &FromPtr = S.Stk.pop<Pointer>();
3006+
3007+
size_t BuffSize = ResultBitWidth / 8;
3008+
llvm::SmallVector<std::byte> Buff(BuffSize);
3009+
bool HasIndeterminateBits = false;
3010+
3011+
if (!DoBitCast(S, OpPC, FromPtr, Buff.data(), BuffSize, HasIndeterminateBits))
3012+
return false;
3013+
3014+
if (!CheckBitCast(S, OpPC, HasIndeterminateBits, TargetIsUCharOrByte))
3015+
return false;
3016+
3017+
if constexpr (std::is_same_v<T, Floating>) {
3018+
assert(false && "Implement bitcasting to a floating type");
3019+
} else {
3020+
assert(!Sem);
3021+
S.Stk.push<T>(T::bitcastFromMemory(Buff.data(), ResultBitWidth));
3022+
}
3023+
return true;
3024+
}
3025+
29983026
//===----------------------------------------------------------------------===//
29993027
// Read opcode arguments
30003028
//===----------------------------------------------------------------------===//

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)