Skip to content

Commit 6c461a3

Browse files
committed
[clang][bytecode] Start implementing __builtin_bit_cast
1 parent 48deb35 commit 6c461a3

File tree

14 files changed

+862
-0
lines changed

14 files changed

+862
-0
lines changed

clang/lib/AST/ByteCode/Boolean.h

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

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

84+
static Boolean bitcastFromMemory(const std::byte *Buff, unsigned BitWidth) {
85+
assert(BitWidth == 8);
86+
bool Val = static_cast<bool>(*Buff);
87+
return Boolean(Val);
88+
}
89+
90+
void bitcastToMemory(std::byte *Buff) { std::memcpy(Buff, &V, sizeof(V)); }
91+
8492
void print(llvm::raw_ostream &OS) const { OS << (V ? "true" : "false"); }
8593
std::string toDiagnosticString(const ASTContext &Ctx) const {
8694
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:
@@ -6400,6 +6403,67 @@ bool Compiler<Emitter>::emitDestruction(const Descriptor *Desc,
64006403
return this->emitRecordDestruction(Desc->ElemRecord, Loc);
64016404
}
64026405

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

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: 30 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,33 @@ 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");
3019+
// assert(Sem);
3020+
// S.Stk.push<Floating>(Floating::bitcastFromMemory(Buff.data(), *Sem));
3021+
} else {
3022+
assert(!Sem);
3023+
S.Stk.push<T>(T::bitcastFromMemory(Buff.data(), ResultBitWidth));
3024+
}
3025+
return true;
3026+
}
3027+
29983028
//===----------------------------------------------------------------------===//
29993029
// Read opcode arguments
30003030
//===----------------------------------------------------------------------===//

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)