511 changes: 505 additions & 6 deletions clang/lib/AST/ExprConstant.cpp

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions clang/lib/AST/ItaniumMangle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3634,6 +3634,7 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity) {
case Expr::AtomicExprClass:
case Expr::SourceLocExprClass:
case Expr::FixedPointLiteralClass:
case Expr::BuiltinBitCastExprClass:
{
if (!NullOut) {
// As bad as this diagnostic is, it's better than crashing.
Expand Down
8 changes: 8 additions & 0 deletions clang/lib/AST/StmtPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1703,6 +1703,14 @@ void StmtPrinter::VisitCXXConstCastExpr(CXXConstCastExpr *Node) {
VisitCXXNamedCastExpr(Node);
}

void StmtPrinter::VisitBuiltinBitCastExpr(BuiltinBitCastExpr *Node) {
OS << "__builtin_bit_cast(";
Node->getTypeInfoAsWritten()->getType().print(OS, Policy);
OS << ", ";
PrintExpr(Node->getSubExpr());
OS << ")";
}

void StmtPrinter::VisitCXXTypeidExpr(CXXTypeidExpr *Node) {
OS << "typeid(";
if (Node->isTypeOperand()) {
Expand Down
5 changes: 5 additions & 0 deletions clang/lib/AST/StmtProfile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1569,6 +1569,11 @@ void StmtProfiler::VisitCXXConstCastExpr(const CXXConstCastExpr *S) {
VisitCXXNamedCastExpr(S);
}

void StmtProfiler::VisitBuiltinBitCastExpr(const BuiltinBitCastExpr *S) {
VisitExpr(S);
VisitType(S->getTypeInfoAsWritten()->getType());
}

void StmtProfiler::VisitUserDefinedLiteral(const UserDefinedLiteral *S) {
VisitCallExpr(S);
}
Expand Down
1 change: 1 addition & 0 deletions clang/lib/CodeGen/CGExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4209,6 +4209,7 @@ LValue CodeGenFunction::EmitCastLValue(const CastExpr *E) {
switch (E->getCastKind()) {
case CK_ToVoid:
case CK_BitCast:
case CK_LValueToRValueBitCast:
case CK_ArrayToPointerDecay:
case CK_FunctionToPointerDecay:
case CK_NullToMemberPointer:
Expand Down
19 changes: 19 additions & 0 deletions clang/lib/CodeGen/CGExprAgg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -711,6 +711,25 @@ void AggExprEmitter::VisitCastExpr(CastExpr *E) {
break;
}

case CK_LValueToRValueBitCast: {
if (Dest.isIgnored()) {
CGF.EmitAnyExpr(E->getSubExpr(), AggValueSlot::ignored(),
/*ignoreResult=*/true);
break;
}

LValue SourceLV = CGF.EmitLValue(E->getSubExpr());
Address SourceAddress =
Builder.CreateElementBitCast(SourceLV.getAddress(), CGF.Int8Ty);
Address DestAddress =
Builder.CreateElementBitCast(Dest.getAddress(), CGF.Int8Ty);
llvm::Value *SizeVal = llvm::ConstantInt::get(
CGF.SizeTy,
CGF.getContext().getTypeSizeInChars(E->getType()).getQuantity());
Builder.CreateMemCpy(DestAddress, SourceAddress, SizeVal);
break;
}

case CK_DerivedToBase:
case CK_BaseToDerived:
case CK_UncheckedDerivedToBase: {
Expand Down
9 changes: 9 additions & 0 deletions clang/lib/CodeGen/CGExprComplex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,15 @@ ComplexPairTy ComplexExprEmitter::EmitCast(CastKind CK, Expr *Op,
return EmitLoadOfLValue(CGF.MakeAddrLValue(V, DestTy), Op->getExprLoc());
}

case CK_LValueToRValueBitCast: {
LValue SourceLVal = CGF.EmitLValue(Op);
Address Addr = Builder.CreateElementBitCast(SourceLVal.getAddress(),
CGF.ConvertTypeForMem(DestTy));
LValue DestLV = CGF.MakeAddrLValue(Addr, DestTy);
DestLV.setTBAAInfo(TBAAAccessInfo::getMayAliasInfo());
return EmitLoadOfLValue(DestLV, Op->getExprLoc());
}

case CK_BitCast:
case CK_BaseToDerived:
case CK_DerivedToBase:
Expand Down
1 change: 1 addition & 0 deletions clang/lib/CodeGen/CGExprConstant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1115,6 +1115,7 @@ class ConstExprEmitter :
case CK_ToVoid:
case CK_Dynamic:
case CK_LValueBitCast:
case CK_LValueToRValueBitCast:
case CK_NullToMemberPointer:
case CK_UserDefinedConversion:
case CK_CPointerToObjCPointerCast:
Expand Down
9 changes: 9 additions & 0 deletions clang/lib/CodeGen/CGExprScalar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2033,6 +2033,15 @@ Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) {
return EmitLoadOfLValue(LV, CE->getExprLoc());
}

case CK_LValueToRValueBitCast: {
LValue SourceLVal = CGF.EmitLValue(E);
Address Addr = Builder.CreateElementBitCast(SourceLVal.getAddress(),
CGF.ConvertTypeForMem(DestTy));
LValue DestLV = CGF.MakeAddrLValue(Addr, DestTy);
DestLV.setTBAAInfo(TBAAAccessInfo::getMayAliasInfo());
return EmitLoadOfLValue(DestLV, CE->getExprLoc());
}

case CK_CPointerToObjCPointerCast:
case CK_BlockPointerToObjCPointerCast:
case CK_AnyPointerToBlockPointerCast:
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Edit/RewriteObjCFoundationAPI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1042,6 +1042,7 @@ static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg,
case CK_Dependent:
case CK_BitCast:
case CK_LValueBitCast:
case CK_LValueToRValueBitCast:
case CK_BaseToDerived:
case CK_DerivedToBase:
case CK_UncheckedDerivedToBase:
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Lex/PPMacroExpansion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1630,6 +1630,7 @@ void Preprocessor::ExpandBuiltinMacro(Token &Tok) {
.Case("__builtin_FILE", true)
.Case("__builtin_FUNCTION", true)
.Case("__builtin_COLUMN", true)
.Case("__builtin_bit_cast", true)
.Default(false);
}
});
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/Parse/ParseExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1223,6 +1223,9 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression,
case tok::kw_static_cast:
Res = ParseCXXCasts();
break;
case tok::kw___builtin_bit_cast:
Res = ParseBuiltinBitCast();
break;
case tok::kw_typeid:
Res = ParseCXXTypeid();
break;
Expand Down
34 changes: 34 additions & 0 deletions clang/lib/Parse/ParseExprCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3513,3 +3513,37 @@ Parser::ParseCXXAmbiguousParenExpression(ParenParseOption &ExprType,
ConsumeAnyToken();
return Result;
}

/// Parse a __builtin_bit_cast(T, E).
ExprResult Parser::ParseBuiltinBitCast() {
SourceLocation KWLoc = ConsumeToken();

BalancedDelimiterTracker T(*this, tok::l_paren);
if (T.expectAndConsume(diag::err_expected_lparen_after, "__builtin_bit_cast"))
return ExprError();

// Parse the common declaration-specifiers piece.
DeclSpec DS(AttrFactory);
ParseSpecifierQualifierList(DS);

// Parse the abstract-declarator, if present.
Declarator DeclaratorInfo(DS, DeclaratorContext::TypeNameContext);
ParseDeclarator(DeclaratorInfo);

if (ExpectAndConsume(tok::comma)) {
Diag(Tok.getLocation(), diag::err_expected) << tok::comma;
SkipUntil(tok::r_paren, StopAtSemi);
return ExprError();
}

ExprResult Operand = ParseExpression();

if (T.consumeClose())
return ExprError();

if (Operand.isInvalid() || DeclaratorInfo.isInvalidType())
return ExprError();

return Actions.ActOnBuiltinBitCastExpr(KWLoc, DeclaratorInfo, Operand,
T.getCloseLocation());
}
70 changes: 70 additions & 0 deletions clang/lib/Sema/SemaCast.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ namespace {
void CheckDynamicCast();
void CheckCXXCStyleCast(bool FunctionalCast, bool ListInitialization);
void CheckCStyleCast();
void CheckBuiltinBitCast();

void updatePartOfExplicitCastFlags(CastExpr *CE) {
// Walk down from the CE to the OrigSrcExpr, and mark all immediate
Expand Down Expand Up @@ -331,6 +332,38 @@ Sema::BuildCXXNamedCast(SourceLocation OpLoc, tok::TokenKind Kind,
}
}

ExprResult Sema::ActOnBuiltinBitCastExpr(SourceLocation KWLoc, Declarator &D,
ExprResult Operand,
SourceLocation RParenLoc) {
assert(!D.isInvalidType());

TypeSourceInfo *TInfo = GetTypeForDeclaratorCast(D, Operand.get()->getType());
if (D.isInvalidType())
return ExprError();

return BuildBuiltinBitCastExpr(KWLoc, TInfo, Operand.get(), RParenLoc);
}

ExprResult Sema::BuildBuiltinBitCastExpr(SourceLocation KWLoc,
TypeSourceInfo *TSI, Expr *Operand,
SourceLocation RParenLoc) {
CastOperation Op(*this, TSI->getType(), Operand);
Op.OpRange = SourceRange(KWLoc, RParenLoc);
TypeLoc TL = TSI->getTypeLoc();
Op.DestRange = SourceRange(TL.getBeginLoc(), TL.getEndLoc());

if (!Operand->isTypeDependent() && !TSI->getType()->isDependentType()) {
Op.CheckBuiltinBitCast();
if (Op.SrcExpr.isInvalid())
return ExprError();
}

BuiltinBitCastExpr *BCE =
new (Context) BuiltinBitCastExpr(Op.ResultType, Op.ValueKind, Op.Kind,
Op.SrcExpr.get(), TSI, KWLoc, RParenLoc);
return Op.complete(BCE);
}

/// Try to diagnose a failed overloaded cast. Returns true if
/// diagnostics were emitted.
static bool tryDiagnoseOverloadedCast(Sema &S, CastType CT,
Expand Down Expand Up @@ -2764,6 +2797,43 @@ void CastOperation::CheckCStyleCast() {
checkCastAlign();
}

void CastOperation::CheckBuiltinBitCast() {
QualType SrcType = SrcExpr.get()->getType();
if (SrcExpr.get()->isRValue())
SrcExpr = Self.CreateMaterializeTemporaryExpr(SrcType, SrcExpr.get(),
/*IsLValueReference=*/false);

CharUnits DestSize = Self.Context.getTypeSizeInChars(DestType);
CharUnits SourceSize = Self.Context.getTypeSizeInChars(SrcType);
if (DestSize != SourceSize) {
Self.Diag(OpRange.getBegin(), diag::err_bit_cast_type_size_mismatch)
<< (int)SourceSize.getQuantity() << (int)DestSize.getQuantity();
SrcExpr = ExprError();
return;
}

if (!DestType.isTriviallyCopyableType(Self.Context)) {
Self.Diag(OpRange.getBegin(), diag::err_bit_cast_non_trivially_copyable)
<< 1;
SrcExpr = ExprError();
return;
}

if (!SrcType.isTriviallyCopyableType(Self.Context)) {
Self.Diag(OpRange.getBegin(), diag::err_bit_cast_non_trivially_copyable)
<< 0;
SrcExpr = ExprError();
return;
}

if (Self.Context.hasSameUnqualifiedType(DestType, SrcType)) {
Kind = CK_NoOp;
return;
}

Kind = CK_LValueToRValueBitCast;
}

/// DiagnoseCastQual - Warn whenever casts discards a qualifiers, be it either
/// const, volatile or both.
static void DiagnoseCastQual(Sema &Self, const ExprResult &SrcExpr,
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Sema/SemaExceptionSpec.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1201,6 +1201,7 @@ CanThrowResult Sema::canThrow(const Expr *E) {
case Expr::CoyieldExprClass:
case Expr::CXXConstCastExprClass:
case Expr::CXXReinterpretCastExprClass:
case Expr::BuiltinBitCastExprClass:
case Expr::CXXStdInitializerListExprClass:
case Expr::DesignatedInitExprClass:
case Expr::DesignatedInitUpdateExprClass:
Expand Down
26 changes: 26 additions & 0 deletions clang/lib/Sema/TreeTransform.h
Original file line number Diff line number Diff line change
Expand Up @@ -2652,6 +2652,16 @@ class TreeTransform {
ListInitialization);
}

/// Build a new C++ __builtin_bit_cast expression.
///
/// By default, performs semantic analysis to build the new expression.
/// Subclasses may override this routine to provide different behavior.
ExprResult RebuildBuiltinBitCastExpr(SourceLocation KWLoc,
TypeSourceInfo *TSI, Expr *Sub,
SourceLocation RParenLoc) {
return getSema().BuildBuiltinBitCastExpr(KWLoc, TSI, Sub, RParenLoc);
}

/// Build a new C++ typeid(type) expression.
///
/// By default, performs semantic analysis to build the new expression.
Expand Down Expand Up @@ -10245,6 +10255,22 @@ TreeTransform<Derived>::TransformCXXNamedCastExpr(CXXNamedCastExpr *E) {
E->getAngleBrackets().getEnd(), SubExpr.get(), E->getRParenLoc());
}

template<typename Derived>
ExprResult
TreeTransform<Derived>::TransformBuiltinBitCastExpr(BuiltinBitCastExpr *BCE) {
TypeSourceInfo *TSI =
getDerived().TransformType(BCE->getTypeInfoAsWritten());
if (!TSI)
return ExprError();

ExprResult Sub = getDerived().TransformExpr(BCE->getSubExpr());
if (Sub.isInvalid())
return ExprError();

return getDerived().RebuildBuiltinBitCastExpr(BCE->getBeginLoc(), TSI,
Sub.get(), BCE->getEndLoc());
}

template<typename Derived>
ExprResult
TreeTransform<Derived>::TransformCXXStaticCastExpr(CXXStaticCastExpr *E) {
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/Serialization/ASTReaderStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1510,6 +1510,12 @@ void ASTStmtReader::VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr *E) {
E->setRParenLoc(ReadSourceLocation());
}

void ASTStmtReader::VisitBuiltinBitCastExpr(BuiltinBitCastExpr *E) {
VisitExplicitCastExpr(E);
E->KWLoc = ReadSourceLocation();
E->RParenLoc = ReadSourceLocation();
}

void ASTStmtReader::VisitUserDefinedLiteral(UserDefinedLiteral *E) {
VisitCallExpr(E);
E->UDSuffixLoc = ReadSourceLocation();
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/Serialization/ASTWriterStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1451,6 +1451,12 @@ void ASTStmtWriter::VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr *E) {
Code = serialization::EXPR_CXX_FUNCTIONAL_CAST;
}

void ASTStmtWriter::VisitBuiltinBitCastExpr(BuiltinBitCastExpr *E) {
VisitExplicitCastExpr(E);
Record.AddSourceLocation(E->getBeginLoc());
Record.AddSourceLocation(E->getEndLoc());
}

void ASTStmtWriter::VisitUserDefinedLiteral(UserDefinedLiteral *E) {
VisitCallExpr(E);
Record.AddSourceLocation(E->UDSuffixLoc);
Expand Down
1 change: 1 addition & 0 deletions clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1687,6 +1687,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::CXXReinterpretCastExprClass:
case Stmt::CXXConstCastExprClass:
case Stmt::CXXFunctionalCastExprClass:
case Stmt::BuiltinBitCastExprClass:
case Stmt::ObjCBridgedCastExprClass: {
Bldr.takeNodes(Pred);
const auto *C = cast<CastExpr>(S);
Expand Down
1 change: 1 addition & 0 deletions clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,7 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex,
case CK_Dependent:
case CK_ArrayToPointerDecay:
case CK_BitCast:
case CK_LValueToRValueBitCast:
case CK_AddressSpaceConversion:
case CK_BooleanToSignedIntegral:
case CK_IntegralToPointer:
Expand Down
19 changes: 19 additions & 0 deletions clang/test/CodeGenCXX/builtin-bit-cast-no-tbaa.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// RUN: %clang_cc1 -O3 -std=c++2a -S -emit-llvm -o - -disable-llvm-passes -triple x86_64-apple-macos10.14 %s | FileCheck %s

void test_scalar() {
// CHECK-LABEL: define void @_Z11test_scalarv
__builtin_bit_cast(float, 42);

// CHECK: load float, float* {{.*}}, align 4, !tbaa ![[MAY_ALIAS_TBAA:.*]]
}

void test_scalar2() {
// CHECK-LABEL: define void @_Z12test_scalar2v
struct S {int m;};
__builtin_bit_cast(int, S{42});

// CHECK: load i32, i32* {{.*}}, align 4, !tbaa ![[MAY_ALIAS_TBAA]]
}

// CHECK: ![[CHAR_TBAA:.*]] = !{!"omnipotent char", {{.*}}, i64 0}
// CHECK: ![[MAY_ALIAS_TBAA]] = !{![[CHAR_TBAA]], ![[CHAR_TBAA]], i64 0}
106 changes: 106 additions & 0 deletions clang/test/CodeGenCXX/builtin-bit-cast.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
// RUN: %clang_cc1 -std=c++2a -S -emit-llvm -o - -disable-llvm-passes -triple x86_64-apple-macos10.14 %s | FileCheck %s

void test_scalar(int &oper) {
// CHECK-LABEL: define void @_Z11test_scalarRi
__builtin_bit_cast(float, oper);

// CHECK: [[OPER:%.*]] = alloca i32*
// CHECK: [[REF:%.*]] = load i32*, i32**
// CHECK-NEXT: [[CASTED:%.*]] = bitcast i32* [[REF]] to float*
// CHECK-NEXT: load float, float* [[CASTED]]
}

struct two_ints {
int x;
int y;
};

unsigned long test_aggregate_to_scalar(two_ints &ti) {
// CHECK-LABEL: define i64 @_Z24test_aggregate_to_scalarR8two_ints
return __builtin_bit_cast(unsigned long, ti);

// CHECK: [[TI_ADDR:%.*]] = alloca %struct.two_ints*, align 8
// CHECK: [[TI_LOAD:%.*]] = load %struct.two_ints*, %struct.two_ints** [[TI_ADDR]]
// CHECK-NEXT: [[CASTED:%.*]] = bitcast %struct.two_ints* [[TI_LOAD]] to i64*
// CHECK-NEXT: load i64, i64* [[CASTED]]
}

struct two_floats {
float x;
float y;
};

two_floats test_aggregate_record(two_ints& ti) {
// CHECK-LABEL: define <2 x float> @_Z21test_aggregate_recordR8two_int
return __builtin_bit_cast(two_floats, ti);

// CHECK: [[RETVAL:%.*]] = alloca %struct.two_floats, align 4
// CHECK: [[TI:%.*]] = alloca %struct.two_ints*, align 8

// CHECK: [[LOAD_TI:%.*]] = load %struct.two_ints*, %struct.two_ints** [[TI]]
// CHECK: [[MEMCPY_SRC:%.*]] = bitcast %struct.two_ints* [[LOAD_TI]] to i8*
// CHECK: [[MEMCPY_DST:%.*]] = bitcast %struct.two_floats* [[RETVAL]] to i8*
// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 [[MEMCPY_DST]], i8* align 4 [[MEMCPY_SRC]]
}

two_floats test_aggregate_array(int (&ary)[2]) {
// CHECK-LABEL: define <2 x float> @_Z20test_aggregate_arrayRA2_i
return __builtin_bit_cast(two_floats, ary);

// CHECK: [[RETVAL:%.*]] = alloca %struct.two_floats, align 4
// CHECK: [[ARY:%.*]] = alloca [2 x i32]*, align 8

// CHECK: [[LOAD_ARY:%.*]] = load [2 x i32]*, [2 x i32]** [[ARY]]
// CHECK: [[MEMCPY_SRC:%.*]] = bitcast [2 x i32]* [[LOAD_ARY]] to i8*
// CHECK: [[MEMCPY_DST:%.*]] = bitcast %struct.two_floats* [[RETVAL]] to i8*
// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 [[MEMCPY_DST]], i8* align 4 [[MEMCPY_SRC]]
}

two_ints test_scalar_to_aggregate(unsigned long ul) {
// CHECK-LABEL: define i64 @_Z24test_scalar_to_aggregatem
return __builtin_bit_cast(two_ints, ul);

// CHECK: [[TI:%.*]] = alloca %struct.two_ints, align 4
// CHECK: [[TITMP:%.*]] = bitcast %struct.two_ints* [[TI]] to i8*
// CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 [[TITMP]]
}

unsigned long test_complex(_Complex unsigned &cu) {
// CHECK-LABEL: define i64 @_Z12test_complexRCj
return __builtin_bit_cast(unsigned long, cu);

// CHECK: [[REF_ALLOCA:%.*]] = alloca { i32, i32 }*, align 8
// CHECK-NEXT: store { i32, i32 }* {{.*}}, { i32, i32 }** [[REF_ALLOCA]]
// CHECK-NEXT: [[REF:%.*]] = load { i32, i32 }*, { i32, i32 }** [[REF_ALLOCA]]
// CHECK-NEXT: [[CASTED:%.*]] = bitcast { i32, i32 }* [[REF]] to i64*
// CHECK-NEXT: load i64, i64* [[CASTED]], align 4
}

_Complex unsigned test_to_complex(unsigned long &ul) {
// CHECK-LABEL: define i64 @_Z15test_to_complexRm

return __builtin_bit_cast(_Complex unsigned, ul);

// CHECK: [[REF:%.*]] = alloca i64*
// CHECK: [[LOAD_REF:%.*]] = load i64*, i64** [[REF]]
// CHECK: [[CASTED:%.*]] = bitcast i64* [[LOAD_REF]] to { i32, i32 }*
}

unsigned long test_array(int (&ary)[2]) {
// CHECK-LABEL: define i64 @_Z10test_arrayRA2_i
return __builtin_bit_cast(unsigned long, ary);

// CHECK: [[REF_ALLOCA:%.*]] = alloca [2 x i32]*
// CHECK: [[LOAD_REF:%.*]] = load [2 x i32]*, [2 x i32]** [[REF_ALLOCA]]
// CHECK: [[CASTED:%.*]] = bitcast [2 x i32]* [[LOAD_REF]] to i64*
// CHECK: load i64, i64* [[CASTED]], align 4
}

two_ints test_rvalue_aggregate() {
// CHECK-LABEL: define i64 @_Z21test_rvalue_aggregate
return __builtin_bit_cast(two_ints, 42ul);

// CHECK: [[TI:%.*]] = alloca %struct.two_ints, align 4
// CHECK: [[CASTED:%.*]] = bitcast %struct.two_ints* [[TI]] to i8*
// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 [[CASTED]]
}
39 changes: 39 additions & 0 deletions clang/test/SemaCXX/builtin-bit-cast.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// RUN: %clang_cc1 -verify -std=c++2a -fsyntax-only -triple x86_64-apple-macosx10.14.0 %s
// RUN: %clang_cc1 -verify -std=c++2a -fsyntax-only -triple x86_64-apple-macosx10.14.0 %s -fno-signed-char

#if !__has_builtin(__builtin_bit_cast)
#error
#endif

template <class T, T v>
T instantiate() {
return __builtin_bit_cast(T, v);
}

int x = instantiate<int, 32>();

struct secret_ctor {
char member;

private: secret_ctor() = default;
};

void test1() {
secret_ctor c = __builtin_bit_cast(secret_ctor, (char)0);
}

void test2() {
constexpr int i = 0;
// expected-error@+1{{__builtin_bit_cast source size does not equal destination size (4 vs 1)}}
constexpr char c = __builtin_bit_cast(char, i);
}

struct not_trivially_copyable {
virtual void foo() {}
};

// expected-error@+1{{__builtin_bit_cast source type must be trivially copyable}}
constexpr unsigned long ul = __builtin_bit_cast(unsigned long, not_trivially_copyable{});

// expected-error@+1 {{__builtin_bit_cast destination type must be trivially copyable}}
constexpr long us = __builtin_bit_cast(unsigned long &, 0L);
383 changes: 383 additions & 0 deletions clang/test/SemaCXX/constexpr-builtin-bit-cast.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,383 @@
// RUN: %clang_cc1 -verify -std=c++2a -fsyntax-only -triple x86_64-apple-macosx10.14.0 %s
// RUN: %clang_cc1 -verify -std=c++2a -fsyntax-only -triple x86_64-apple-macosx10.14.0 %s -fno-signed-char
// RUN: %clang_cc1 -verify -std=c++2a -fsyntax-only -triple aarch64_be-linux-gnu %s

#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
# define LITTLE_END 1
#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
# define LITTLE_END 0
#else
# error "huh?"
#endif

template <class T, class V> struct is_same {
static constexpr bool value = false;
};
template <class T> struct is_same<T, T> {
static constexpr bool value = true;
};

static_assert(sizeof(int) == 4);
static_assert(sizeof(long long) == 8);

template <class To, class From>
constexpr To bit_cast(const From &from) {
static_assert(sizeof(To) == sizeof(From));
#ifdef __CHAR_UNSIGNED__
// expected-note@+4 2 {{indeterminate value can only initialize an object of type 'unsigned char', 'char', or 'std::byte'; 'signed char' is invalid}}
#else
// expected-note@+2 2 {{indeterminate value can only initialize an object of type 'unsigned char' or 'std::byte'; 'signed char' is invalid}}
#endif
return __builtin_bit_cast(To, from);
}

template <class Intermediate, class Init>
constexpr bool round_trip(const Init &init) {
return bit_cast<Init>(bit_cast<Intermediate>(init)) == init;
}

void test_int() {
static_assert(round_trip<unsigned>((int)-1));
static_assert(round_trip<unsigned>((int)0x12345678));
static_assert(round_trip<unsigned>((int)0x87654321));
static_assert(round_trip<unsigned>((int)0x0C05FEFE));
}

void test_array() {
constexpr unsigned char input[] = {0xCA, 0xFE, 0xBA, 0xBE};
constexpr unsigned expected = LITTLE_END ? 0xBEBAFECA : 0xCAFEBABE;
static_assert(bit_cast<unsigned>(input) == expected);
}

void test_record() {
struct int_splicer {
unsigned x;
unsigned y;

constexpr bool operator==(const int_splicer &other) const {
return other.x == x && other.y == y;
}
};

constexpr int_splicer splice{0x0C05FEFE, 0xCAFEBABE};

static_assert(bit_cast<unsigned long long>(splice) == LITTLE_END
? 0xCAFEBABE0C05FEFE
: 0x0C05FEFECAFEBABE);

static_assert(bit_cast<int_splicer>(0xCAFEBABE0C05FEFE).x == LITTLE_END
? 0x0C05FEFE
: 0xCAFEBABE);

static_assert(round_trip<unsigned long long>(splice));
static_assert(round_trip<long long>(splice));

struct base2 {
};

struct base3 {
unsigned z;
};

struct bases : int_splicer, base2, base3 {
unsigned doublez;
};

struct tuple4 {
unsigned x, y, z, doublez;

constexpr bool operator==(tuple4 const &other) const {
return x == other.x && y == other.y &&
z == other.z && doublez == other.doublez;
}
};
constexpr bases b = {{1, 2}, {}, {3}, 4};
constexpr tuple4 t4 = bit_cast<tuple4>(b);
static_assert(t4 == tuple4{1, 2, 3, 4});
static_assert(round_trip<tuple4>(b));
}

void test_partially_initialized() {
struct pad {
signed char x;
int y;
};

struct no_pad {
signed char x;
signed char p1, p2, p3;
int y;
};

static_assert(sizeof(pad) == sizeof(no_pad));

constexpr pad pir{4, 4};
// expected-error@+2 {{constexpr variable 'piw' must be initialized by a constant expression}}
// expected-note@+1 {{in call to 'bit_cast(pir)'}}
constexpr int piw = bit_cast<no_pad>(pir).x;

// expected-error@+2 {{constexpr variable 'bad' must be initialized by a constant expression}}
// expected-note@+1 {{in call to 'bit_cast(pir)'}}
constexpr no_pad bad = bit_cast<no_pad>(pir);

constexpr pad fine = bit_cast<pad>(no_pad{1, 2, 3, 4, 5});
static_assert(fine.x == 1 && fine.y == 5);
}

void no_bitfields() {
// FIXME!
struct S {
unsigned char x : 8;
};

struct G {
unsigned char x : 8;
};

constexpr S s{0};
// expected-error@+2 {{constexpr variable 'g' must be initialized by a constant expression}}
// expected-note@+1 {{constexpr bit_cast involving bit-field is not yet supported}}
constexpr G g = __builtin_bit_cast(G, s);
}

void array_members() {
struct S {
int ar[3];

constexpr bool operator==(const S &rhs) {
return ar[0] == rhs.ar[0] && ar[1] == rhs.ar[1] && ar[2] == rhs.ar[2];
}
};

struct G {
int a, b, c;

constexpr bool operator==(const G &rhs) {
return a == rhs.a && b == rhs.b && c == rhs.c;
}
};

constexpr S s{{1, 2, 3}};
constexpr G g = bit_cast<G>(s);
static_assert(g.a == 1 && g.b == 2 && g.c == 3);

static_assert(round_trip<G>(s));
static_assert(round_trip<S>(g));
}

void bad_types() {
union X {
int x;
};

struct G {
int g;
};
// expected-error@+2 {{constexpr variable 'g' must be initialized by a constant expression}}
// expected-note@+1 {{bit_cast from a union type is not allowed in a constant expression}}
constexpr G g = __builtin_bit_cast(G, X{0});
// expected-error@+2 {{constexpr variable 'x' must be initialized by a constant expression}}
// expected-note@+1 {{bit_cast to a union type is not allowed in a constant expression}}
constexpr X x = __builtin_bit_cast(X, G{0});

struct has_pointer {
// expected-note@+1 2 {{invalid type 'int *' is a member of 'has_pointer'}}
int *ptr;
};

// expected-error@+2 {{constexpr variable 'ptr' must be initialized by a constant expression}}
// expected-note@+1 {{bit_cast from a pointer type is not allowed in a constant expression}}
constexpr unsigned long ptr = __builtin_bit_cast(unsigned long, has_pointer{0});
// expected-error@+2 {{constexpr variable 'hptr' must be initialized by a constant expression}}
// expected-note@+1 {{bit_cast to a pointer type is not allowed in a constant expression}}
constexpr has_pointer hptr = __builtin_bit_cast(has_pointer, 0ul);
}

void backtrace() {
struct A {
// expected-note@+1 {{invalid type 'int *' is a member of 'A'}}
int *ptr;
};

struct B {
// expected-note@+1 {{invalid type 'A [10]' is a member of 'B'}}
A as[10];
};

// expected-note@+1 {{invalid type 'B' is a base of 'C'}}
struct C : B {
};

struct E {
unsigned long ar[10];
};

// expected-error@+2 {{constexpr variable 'e' must be initialized by a constant expression}}
// expected-note@+1 {{bit_cast from a pointer type is not allowed in a constant expression}}
constexpr E e = __builtin_bit_cast(E, C{});
}

void test_array_fill() {
constexpr unsigned char a[4] = {1, 2};
constexpr unsigned int i = bit_cast<unsigned int>(a);
static_assert(i == LITTLE_END ? 0x00000201 : 0x01020000, "");
}

typedef decltype(nullptr) nullptr_t;

#ifdef __CHAR_UNSIGNED__
// expected-note@+5 {{indeterminate value can only initialize an object of type 'unsigned char', 'char', or 'std::byte'; 'unsigned long' is invalid}}
#else
// expected-note@+3 {{indeterminate value can only initialize an object of type 'unsigned char' or 'std::byte'; 'unsigned long' is invalid}}
#endif
// expected-error@+1 {{constexpr variable 'test_from_nullptr' must be initialized by a constant expression}}
constexpr unsigned long test_from_nullptr = __builtin_bit_cast(unsigned long, nullptr);

constexpr int test_from_nullptr_pass = (__builtin_bit_cast(unsigned char[8], nullptr), 0);

constexpr int test_to_nullptr() {
nullptr_t npt = __builtin_bit_cast(nullptr_t, 0ul);

struct indet_mem {
unsigned char data[sizeof(void *)];
};
indet_mem im = __builtin_bit_cast(indet_mem, nullptr);
nullptr_t npt2 = __builtin_bit_cast(nullptr_t, im);

return 0;
}

constexpr int ttn = test_to_nullptr();

// expected-warning@+2 {{returning reference to local temporary object}}
// expected-note@+1 {{temporary created here}}
constexpr const long &returns_local() { return 0L; }

// expected-error@+2 {{constexpr variable 'test_nullptr_bad' must be initialized by a constant expression}}
// expected-note@+1 {{read of temporary whose lifetime has ended}}
constexpr nullptr_t test_nullptr_bad = __builtin_bit_cast(nullptr_t, returns_local());

constexpr int test_indeterminate(bool read_indet) {
struct pad {
char a;
int b;
};

struct no_pad {
char a;
unsigned char p1, p2, p3;
int b;
};

pad p{1, 2};
no_pad np = bit_cast<no_pad>(p);

int tmp = np.a + np.b;

unsigned char& indet_ref = np.p1;

if (read_indet) {
// expected-note@+1 {{read of uninitialized object is not allowed in a constant expression}}
tmp = indet_ref;
}

indet_ref = 0;

return 0;
}

constexpr int run_test_indeterminate = test_indeterminate(false);
// expected-error@+2 {{constexpr variable 'run_test_indeterminate2' must be initialized by a constant expression}}
// expected-note@+1 {{in call to 'test_indeterminate(true)'}}
constexpr int run_test_indeterminate2 = test_indeterminate(true);

struct ref_mem {
const int &rm;
};

constexpr int global_int = 0;

// expected-error@+2 {{constexpr variable 'run_ref_mem' must be initialized by a constant expression}}
// expected-note@+1 {{bit_cast from a type with a reference member is not allowed in a constant expression}}
constexpr unsigned long run_ref_mem = __builtin_bit_cast(
unsigned long, ref_mem{global_int});

union u {
int im;
};

// expected-error@+2 {{constexpr variable 'run_u' must be initialized by a constant expression}}
// expected-note@+1 {{bit_cast from a union type is not allowed in a constant expression}}
constexpr int run_u = __builtin_bit_cast(int, u{32});

struct vol_mem {
volatile int x;
};

// expected-error@+2 {{constexpr variable 'run_vol_mem' must be initialized by a constant expression}}
// expected-note@+1 {{non-literal type 'vol_mem' cannot be used in a constant expression}}
constexpr int run_vol_mem = __builtin_bit_cast(int, vol_mem{43});

struct mem_ptr {
int vol_mem::*x; // expected-note{{invalid type 'int vol_mem::*' is a member of 'mem_ptr'}}
};
// expected-error@+2 {{constexpr variable 'run_mem_ptr' must be initialized by a constant expression}}
// expected-note@+1 {{bit_cast from a member pointer type is not allowed in a constant expression}}
constexpr int run_mem_ptr = __builtin_bit_cast(unsigned long, mem_ptr{nullptr});

struct A { char c; /* char padding : 8; */ short s; };
struct B { unsigned char x[4]; };

constexpr B one() {
A a = {1, 2};
return bit_cast<B>(a);
}
constexpr char good_one = one().x[0] + one().x[2] + one().x[3];
// expected-error@+2 {{constexpr variable 'bad_one' must be initialized by a constant expression}}
// expected-note@+1 {{read of uninitialized object is not allowed in a constant expression}}
constexpr char bad_one = one().x[1];

constexpr A two() {
B b = one(); // b.x[1] is indeterminate.
b.x[0] = 'a';
b.x[2] = 1;
b.x[3] = 2;
return bit_cast<A>(b);
}
constexpr short good_two = two().c + two().s;

namespace std {
enum byte : unsigned char {};
}

enum my_byte : unsigned char {};

struct pad {
char a;
int b;
};

constexpr int ok_byte = (__builtin_bit_cast(std::byte[8], pad{1, 2}), 0);
constexpr int ok_uchar = (__builtin_bit_cast(unsigned char[8], pad{1, 2}), 0);

#ifdef __CHAR_UNSIGNED__
// expected-note@+5 {{indeterminate value can only initialize an object of type 'unsigned char', 'char', or 'std::byte'; 'my_byte' is invalid}}}}
#else
// expected-note@+3 {{indeterminate value can only initialize an object of type 'unsigned char' or 'std::byte'; 'my_byte' is invalid}}
#endif
// expected-error@+1 {{constexpr variable 'bad_my_byte' must be initialized by a constant expression}}
constexpr int bad_my_byte = (__builtin_bit_cast(my_byte[8], pad{1, 2}), 0);
#ifndef __CHAR_UNSIGNED__
// expected-error@+3 {{constexpr variable 'bad_char' must be initialized by a constant expression}}
// expected-note@+2 {{indeterminate value can only initialize an object of type 'unsigned char' or 'std::byte'; 'char' is invalid}}
#endif
constexpr int bad_char = (__builtin_bit_cast(char[8], pad{1, 2}), 0);

struct pad_buffer { unsigned char data[sizeof(pad)]; };
constexpr bool test_pad_buffer() {
pad x = {1, 2};
pad_buffer y = __builtin_bit_cast(pad_buffer, x);
pad z = __builtin_bit_cast(pad, y);
return x.a == z.a && x.b == z.b;
}
static_assert(test_pad_buffer());
2 changes: 2 additions & 0 deletions clang/tools/libclang/CIndex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5194,6 +5194,8 @@ CXString clang_getCursorKindSpelling(enum CXCursorKind Kind) {
return cxstring::createRef("CallExpr");
case CXCursor_ObjCMessageExpr:
return cxstring::createRef("ObjCMessageExpr");
case CXCursor_BuiltinBitCastExpr:
return cxstring::createRef("BuiltinBitCastExpr");
case CXCursor_UnexposedStmt:
return cxstring::createRef("UnexposedStmt");
case CXCursor_DeclStmt:
Expand Down
2 changes: 2 additions & 0 deletions clang/tools/libclang/CXCursor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -716,6 +716,8 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent,
case Stmt::OMPTargetTeamsDistributeSimdDirectiveClass:
K = CXCursor_OMPTargetTeamsDistributeSimdDirective;
break;
case Stmt::BuiltinBitCastExprClass:
K = CXCursor_BuiltinBitCastExpr;
}

CXCursor C = { K, 0, { Parent, S, TU } };
Expand Down
11 changes: 10 additions & 1 deletion llvm/include/llvm/ADT/APInt.h
Original file line number Diff line number Diff line change
Expand Up @@ -2212,6 +2212,15 @@ Optional<APInt> SolveQuadraticEquationWrap(APInt A, APInt B, APInt C,
// See friend declaration above. This additional declaration is required in
// order to compile LLVM with IBM xlC compiler.
hash_code hash_value(const APInt &Arg);
} // End of llvm namespace

/// StoreIntToMemory - Fills the StoreBytes bytes of memory starting from Dst
/// with the integer held in IntVal.
void StoreIntToMemory(const APInt &IntVal, uint8_t *Dst, unsigned StoreBytes);

/// LoadIntFromMemory - Loads the integer stored in the LoadBytes bytes starting
/// from Src into IntVal, which is assumed to be wide enough and to hold zero.
void LoadIntFromMemory(APInt &IntVal, uint8_t *Src, unsigned LoadBytes);

} // namespace llvm

#endif
53 changes: 0 additions & 53 deletions llvm/lib/ExecutionEngine/ExecutionEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1019,32 +1019,6 @@ GenericValue ExecutionEngine::getConstantValue(const Constant *C) {
return Result;
}

/// StoreIntToMemory - Fills the StoreBytes bytes of memory starting from Dst
/// with the integer held in IntVal.
static void StoreIntToMemory(const APInt &IntVal, uint8_t *Dst,
unsigned StoreBytes) {
assert((IntVal.getBitWidth()+7)/8 >= StoreBytes && "Integer too small!");
const uint8_t *Src = (const uint8_t *)IntVal.getRawData();

if (sys::IsLittleEndianHost) {
// Little-endian host - the source is ordered from LSB to MSB. Order the
// destination from LSB to MSB: Do a straight copy.
memcpy(Dst, Src, StoreBytes);
} else {
// Big-endian host - the source is an array of 64 bit words ordered from
// LSW to MSW. Each word is ordered from MSB to LSB. Order the destination
// from MSB to LSB: Reverse the word order, but not the bytes in a word.
while (StoreBytes > sizeof(uint64_t)) {
StoreBytes -= sizeof(uint64_t);
// May not be aligned so use memcpy.
memcpy(Dst + StoreBytes, Src, sizeof(uint64_t));
Src += sizeof(uint64_t);
}

memcpy(Dst, Src + sizeof(uint64_t) - StoreBytes, StoreBytes);
}
}

void ExecutionEngine::StoreValueToMemory(const GenericValue &Val,
GenericValue *Ptr, Type *Ty) {
const unsigned StoreBytes = getDataLayout().getTypeStoreSize(Ty);
Expand Down Expand Up @@ -1092,33 +1066,6 @@ void ExecutionEngine::StoreValueToMemory(const GenericValue &Val,
std::reverse((uint8_t*)Ptr, StoreBytes + (uint8_t*)Ptr);
}

/// LoadIntFromMemory - Loads the integer stored in the LoadBytes bytes starting
/// from Src into IntVal, which is assumed to be wide enough and to hold zero.
static void LoadIntFromMemory(APInt &IntVal, uint8_t *Src, unsigned LoadBytes) {
assert((IntVal.getBitWidth()+7)/8 >= LoadBytes && "Integer too small!");
uint8_t *Dst = reinterpret_cast<uint8_t *>(
const_cast<uint64_t *>(IntVal.getRawData()));

if (sys::IsLittleEndianHost)
// Little-endian host - the destination must be ordered from LSB to MSB.
// The source is ordered from LSB to MSB: Do a straight copy.
memcpy(Dst, Src, LoadBytes);
else {
// Big-endian - the destination is an array of 64 bit words ordered from
// LSW to MSW. Each word must be ordered from MSB to LSB. The source is
// ordered from MSB to LSB: Reverse the word order, but not the bytes in
// a word.
while (LoadBytes > sizeof(uint64_t)) {
LoadBytes -= sizeof(uint64_t);
// May not be aligned so use memcpy.
memcpy(Dst, Src + LoadBytes, sizeof(uint64_t));
Dst += sizeof(uint64_t);
}

memcpy(Dst + sizeof(uint64_t) - LoadBytes, Src, LoadBytes);
}
}

/// FIXME: document
///
void ExecutionEngine::LoadValueFromMemory(GenericValue &Result,
Expand Down
53 changes: 53 additions & 0 deletions llvm/lib/Support/APInt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2934,3 +2934,56 @@ llvm::APIntOps::SolveQuadraticEquationWrap(APInt A, APInt B, APInt C,
LLVM_DEBUG(dbgs() << __func__ << ": solution (wrap): " << X << '\n');
return X;
}

/// StoreIntToMemory - Fills the StoreBytes bytes of memory starting from Dst
/// with the integer held in IntVal.
void llvm::StoreIntToMemory(const APInt &IntVal, uint8_t *Dst,
unsigned StoreBytes) {
assert((IntVal.getBitWidth()+7)/8 >= StoreBytes && "Integer too small!");
const uint8_t *Src = (const uint8_t *)IntVal.getRawData();

if (sys::IsLittleEndianHost) {
// Little-endian host - the source is ordered from LSB to MSB. Order the
// destination from LSB to MSB: Do a straight copy.
memcpy(Dst, Src, StoreBytes);
} else {
// Big-endian host - the source is an array of 64 bit words ordered from
// LSW to MSW. Each word is ordered from MSB to LSB. Order the destination
// from MSB to LSB: Reverse the word order, but not the bytes in a word.
while (StoreBytes > sizeof(uint64_t)) {
StoreBytes -= sizeof(uint64_t);
// May not be aligned so use memcpy.
memcpy(Dst + StoreBytes, Src, sizeof(uint64_t));
Src += sizeof(uint64_t);
}

memcpy(Dst, Src + sizeof(uint64_t) - StoreBytes, StoreBytes);
}
}

/// LoadIntFromMemory - Loads the integer stored in the LoadBytes bytes starting
/// from Src into IntVal, which is assumed to be wide enough and to hold zero.
void llvm::LoadIntFromMemory(APInt &IntVal, uint8_t *Src, unsigned LoadBytes) {
assert((IntVal.getBitWidth()+7)/8 >= LoadBytes && "Integer too small!");
uint8_t *Dst = reinterpret_cast<uint8_t *>(
const_cast<uint64_t *>(IntVal.getRawData()));

if (sys::IsLittleEndianHost)
// Little-endian host - the destination must be ordered from LSB to MSB.
// The source is ordered from LSB to MSB: Do a straight copy.
memcpy(Dst, Src, LoadBytes);
else {
// Big-endian - the destination is an array of 64 bit words ordered from
// LSW to MSW. Each word must be ordered from MSB to LSB. The source is
// ordered from MSB to LSB: Reverse the word order, but not the bytes in
// a word.
while (LoadBytes > sizeof(uint64_t)) {
LoadBytes -= sizeof(uint64_t);
// May not be aligned so use memcpy.
memcpy(Dst, Src + LoadBytes, sizeof(uint64_t));
Dst += sizeof(uint64_t);
}

memcpy(Dst + sizeof(uint64_t) - LoadBytes, Src, LoadBytes);
}
}