Skip to content

Commit e0bb550

Browse files
committed
[Clang] Add __builtin_invoke and recognize std::invoke as a builtin
1 parent 0719879 commit e0bb550

File tree

9 files changed

+499
-102
lines changed

9 files changed

+499
-102
lines changed

clang/include/clang/Basic/Builtins.td

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4272,6 +4272,12 @@ def MoveIfNsoexcept : CxxLibBuiltin<"utility"> {
42724272
let Namespace = "std";
42734273
}
42744274

4275+
def Invoke : Builtin {
4276+
let Spellings = ["__builtin_invoke"];
4277+
let Attributes = [CustomTypeChecking, Constexpr];
4278+
let Prototype = "void(...)";
4279+
}
4280+
42754281
def Annotation : Builtin {
42764282
let Spellings = ["__builtin_annotation"];
42774283
let Attributes = [NoThrow, CustomTypeChecking];

clang/include/clang/Sema/Sema.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2594,6 +2594,8 @@ class Sema final : public SemaBase {
25942594
SourceLocation BuiltinLoc,
25952595
SourceLocation RParenLoc);
25962596

2597+
ExprResult BuiltinInvoke(CallExpr *TheCall);
2598+
25972599
static StringRef GetFormatStringTypeName(FormatStringType FST);
25982600
static FormatStringType GetFormatStringType(StringRef FormatFlavor);
25992601
static FormatStringType GetFormatStringType(const FormatAttr *Format);
@@ -15220,11 +15222,18 @@ class Sema final : public SemaBase {
1522015222
SourceLocation Loc);
1522115223
QualType BuiltinRemoveReference(QualType BaseType, UTTKind UKind,
1522215224
SourceLocation Loc);
15225+
15226+
QualType BuiltinRemoveCVRef(QualType BaseType, SourceLocation Loc) {
15227+
return BuiltinRemoveReference(BaseType, UTTKind::RemoveCVRef, Loc);
15228+
}
15229+
1522315230
QualType BuiltinChangeCVRQualifiers(QualType BaseType, UTTKind UKind,
1522415231
SourceLocation Loc);
1522515232
QualType BuiltinChangeSignedness(QualType BaseType, UTTKind UKind,
1522615233
SourceLocation Loc);
1522715234

15235+
bool BuiltinIsBaseOf(SourceLocation RhsTLoc, QualType LhsT, QualType RhsT);
15236+
1522815237
/// Ensure that the type T is a literal type.
1522915238
///
1523015239
/// This routine checks whether the type @p T is a literal type. If @p T is an

clang/lib/Parse/ParseDeclCXX.cpp

Lines changed: 1 addition & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1611,29 +1611,7 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
16111611
Tok.isOneOf(
16121612
#define TRANSFORM_TYPE_TRAIT_DEF(_, Trait) tok::kw___##Trait,
16131613
#include "clang/Basic/TransformTypeTraits.def"
1614-
tok::kw___is_abstract,
1615-
tok::kw___is_aggregate,
1616-
tok::kw___is_arithmetic,
1617-
tok::kw___is_array,
1618-
tok::kw___is_assignable,
1619-
tok::kw___is_base_of,
1620-
tok::kw___is_bounded_array,
1621-
tok::kw___is_class,
1622-
tok::kw___is_complete_type,
1623-
tok::kw___is_compound,
1624-
tok::kw___is_const,
1625-
tok::kw___is_constructible,
1626-
tok::kw___is_convertible,
1627-
tok::kw___is_convertible_to,
1628-
tok::kw___is_destructible,
1629-
tok::kw___is_empty,
1630-
tok::kw___is_enum,
1631-
tok::kw___is_floating_point,
1632-
tok::kw___is_final,
1633-
tok::kw___is_function,
1634-
tok::kw___is_fundamental,
1635-
tok::kw___is_integral,
1636-
tok::kw___is_interface_class,
1614+
tok::kw___is_convertible, // Last use in libc++ was removed in 925a11a
16371615
tok::kw___is_literal,
16381616
tok::kw___is_lvalue_expr,
16391617
tok::kw___is_lvalue_reference,

clang/lib/Sema/SemaChecking.cpp

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2368,6 +2368,8 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
23682368
return BuiltinShuffleVector(TheCall);
23692369
// TheCall will be freed by the smart pointer here, but that's fine, since
23702370
// BuiltinShuffleVector guts it, but then doesn't release it.
2371+
case Builtin::BI__builtin_invoke:
2372+
return BuiltinInvoke(TheCall);
23712373
case Builtin::BI__builtin_prefetch:
23722374
if (BuiltinPrefetch(TheCall))
23732375
return ExprError();
@@ -5406,6 +5408,101 @@ ExprResult Sema::ConvertVectorExpr(Expr *E, TypeSourceInfo *TInfo,
54065408
RParenLoc, CurFPFeatureOverrides());
54075409
}
54085410

5411+
ExprResult Sema::BuiltinInvoke(CallExpr *TheCall) {
5412+
auto Loc = TheCall->getBeginLoc();
5413+
auto Args = MutableArrayRef(TheCall->getArgs(), TheCall->getNumArgs());
5414+
assert(llvm::none_of(Args,
5415+
[](Expr *Arg) { return Arg->isTypeDependent(); }));
5416+
5417+
if (Args.size() == 0) {
5418+
Diag(TheCall->getBeginLoc(), diag::err_typecheck_call_too_few_args_at_least)
5419+
<< 0 << 1 << 0 << 0 << TheCall->getSourceRange();
5420+
return ExprError();
5421+
}
5422+
5423+
auto FuncT = Args[0]->getType();
5424+
5425+
if (auto *MPT = FuncT->getAs<MemberPointerType>()) {
5426+
if (Args.size() < 2) {
5427+
Diag(TheCall->getBeginLoc(),
5428+
diag::err_typecheck_call_too_few_args_at_least)
5429+
<< 0 << 2 << 1 << 0 << TheCall->getSourceRange();
5430+
return ExprError();
5431+
}
5432+
5433+
auto *MemPtrClass = MPT->getQualifier()->getAsType();
5434+
auto ObjectT = Args[1]->getType();
5435+
5436+
5437+
if (MPT->isMemberDataPointer() && Args.size() != 2) {
5438+
Diag(TheCall->getBeginLoc(), diag::err_typecheck_call_too_many_args)
5439+
<< 0 << 2 << Args.size() << 0 << TheCall->getSourceRange();
5440+
return ExprError();
5441+
}
5442+
5443+
ExprResult ObjectArg = [&]() -> ExprResult {
5444+
// (1.1): (t1.*f)(t2, …, tN) when f is a pointer to a member function of a
5445+
// class T and is_same_v<T, remove_cvref_t<decltype(t1)>> ||
5446+
// is_base_of_v<T, remove_cvref_t<decltype(t1)>> is true;
5447+
// (1.4): t1.*f when N=1 and f is a pointer to data member of a class T
5448+
// and is_same_v<T, remove_cvref_t<decltype(t1)>> ||
5449+
// is_base_of_v<T, remove_cvref_t<decltype(t1)>> is true;
5450+
if (Context.hasSameType(QualType(MemPtrClass, 0),
5451+
BuiltinRemoveCVRef(ObjectT, Loc)) ||
5452+
BuiltinIsBaseOf(Args[1]->getBeginLoc(), QualType(MemPtrClass, 0),
5453+
BuiltinRemoveCVRef(ObjectT, Loc))) {
5454+
return Args[1];
5455+
}
5456+
5457+
// (t1.get().*f)(t2, …, tN) when f is a pointer to a member function of
5458+
// a class T and remove_cvref_t<decltype(t1)> is a specialization of
5459+
// reference_wrapper;
5460+
if (auto *RD = ObjectT->getAsCXXRecordDecl()) {
5461+
if (RD->isInStdNamespace() &&
5462+
RD->getDeclName().getAsString() == "reference_wrapper") {
5463+
CXXScopeSpec SS;
5464+
IdentifierInfo *GetName = &Context.Idents.get("get");
5465+
UnqualifiedId GetID;
5466+
GetID.setIdentifier(GetName, Loc);
5467+
5468+
auto MemExpr = ActOnMemberAccessExpr(
5469+
getCurScope(), Args[1], Loc, tok::period, SS,
5470+
/*TemplateKWLoc=*/SourceLocation(), GetID, nullptr);
5471+
5472+
if (MemExpr.isInvalid())
5473+
return ExprError();
5474+
5475+
return ActOnCallExpr(getCurScope(), MemExpr.get(), Loc, {}, Loc);
5476+
}
5477+
}
5478+
5479+
// ((*t1).*f)(t2, …, tN) when f is a pointer to a member function of a
5480+
// class T and t1 does not satisfy the previous two items;
5481+
5482+
return ActOnUnaryOp(getCurScope(), Loc, tok::star, Args[1]);
5483+
}();
5484+
5485+
if (ObjectArg.isInvalid())
5486+
return ExprError();
5487+
5488+
auto BinOp = ActOnBinOp(getCurScope(), TheCall->getBeginLoc(),
5489+
tok::periodstar, ObjectArg.get(), Args[0]);
5490+
if (BinOp.isInvalid())
5491+
return ExprError();
5492+
5493+
if (MPT->isMemberDataPointer())
5494+
return BinOp;
5495+
5496+
auto *MemCall = new (Context)
5497+
ParenExpr(SourceLocation(), SourceLocation(), BinOp.get());
5498+
5499+
return ActOnCallExpr(getCurScope(), MemCall, TheCall->getBeginLoc(),
5500+
Args.drop_front(2), TheCall->getRParenLoc());
5501+
}
5502+
return ActOnCallExpr(getCurScope(), Args.front(), TheCall->getBeginLoc(),
5503+
Args.drop_front(), TheCall->getRParenLoc());
5504+
}
5505+
54095506
bool Sema::BuiltinPrefetch(CallExpr *TheCall) {
54105507
unsigned NumArgs = TheCall->getNumArgs();
54115508

clang/lib/Sema/SemaExprCXX.cpp

Lines changed: 54 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -6540,67 +6540,70 @@ ExprResult Sema::ActOnTypeTrait(TypeTrait Kind, SourceLocation KWLoc,
65406540
return BuildTypeTrait(Kind, KWLoc, ConvertedArgs, RParenLoc);
65416541
}
65426542

6543-
static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT, const TypeSourceInfo *Lhs,
6544-
const TypeSourceInfo *Rhs, SourceLocation KeyLoc) {
6545-
QualType LhsT = Lhs->getType();
6546-
QualType RhsT = Rhs->getType();
6543+
bool Sema::BuiltinIsBaseOf(SourceLocation RhsTLoc, QualType LhsT,
6544+
QualType RhsT) {
6545+
// C++0x [meta.rel]p2
6546+
// Base is a base class of Derived without regard to cv-qualifiers or
6547+
// Base and Derived are not unions and name the same class type without
6548+
// regard to cv-qualifiers.
6549+
6550+
const RecordType *lhsRecord = LhsT->getAs<RecordType>();
6551+
const RecordType *rhsRecord = RhsT->getAs<RecordType>();
6552+
if (!rhsRecord || !lhsRecord) {
6553+
const ObjCObjectType *LHSObjTy = LhsT->getAs<ObjCObjectType>();
6554+
const ObjCObjectType *RHSObjTy = RhsT->getAs<ObjCObjectType>();
6555+
if (!LHSObjTy || !RHSObjTy)
6556+
return false;
65476557

6548-
assert(!LhsT->isDependentType() && !RhsT->isDependentType() &&
6549-
"Cannot evaluate traits of dependent types");
6558+
ObjCInterfaceDecl *BaseInterface = LHSObjTy->getInterface();
6559+
ObjCInterfaceDecl *DerivedInterface = RHSObjTy->getInterface();
6560+
if (!BaseInterface || !DerivedInterface)
6561+
return false;
65506562

6551-
switch(BTT) {
6552-
case BTT_IsBaseOf: {
6553-
// C++0x [meta.rel]p2
6554-
// Base is a base class of Derived without regard to cv-qualifiers or
6555-
// Base and Derived are not unions and name the same class type without
6556-
// regard to cv-qualifiers.
6557-
6558-
const RecordType *lhsRecord = LhsT->getAs<RecordType>();
6559-
const RecordType *rhsRecord = RhsT->getAs<RecordType>();
6560-
if (!rhsRecord || !lhsRecord) {
6561-
const ObjCObjectType *LHSObjTy = LhsT->getAs<ObjCObjectType>();
6562-
const ObjCObjectType *RHSObjTy = RhsT->getAs<ObjCObjectType>();
6563-
if (!LHSObjTy || !RHSObjTy)
6564-
return false;
6563+
if (RequireCompleteType(RhsTLoc, RhsT,
6564+
diag::err_incomplete_type_used_in_type_trait_expr))
6565+
return false;
65656566

6566-
ObjCInterfaceDecl *BaseInterface = LHSObjTy->getInterface();
6567-
ObjCInterfaceDecl *DerivedInterface = RHSObjTy->getInterface();
6568-
if (!BaseInterface || !DerivedInterface)
6569-
return false;
6567+
return BaseInterface->isSuperClassOf(DerivedInterface);
6568+
}
65706569

6571-
if (Self.RequireCompleteType(
6572-
Rhs->getTypeLoc().getBeginLoc(), RhsT,
6573-
diag::err_incomplete_type_used_in_type_trait_expr))
6574-
return false;
6570+
assert(Context.hasSameUnqualifiedType(LhsT, RhsT) ==
6571+
(lhsRecord == rhsRecord));
65756572

6576-
return BaseInterface->isSuperClassOf(DerivedInterface);
6577-
}
6573+
// Unions are never base classes, and never have base classes.
6574+
// It doesn't matter if they are complete or not. See PR#41843
6575+
if (lhsRecord && lhsRecord->getDecl()->isUnion())
6576+
return false;
6577+
if (rhsRecord && rhsRecord->getDecl()->isUnion())
6578+
return false;
6579+
6580+
if (lhsRecord == rhsRecord)
6581+
return true;
65786582

6579-
assert(Self.Context.hasSameUnqualifiedType(LhsT, RhsT)
6580-
== (lhsRecord == rhsRecord));
6583+
// C++0x [meta.rel]p2:
6584+
// If Base and Derived are class types and are different types
6585+
// (ignoring possible cv-qualifiers) then Derived shall be a
6586+
// complete type.
6587+
if (RequireCompleteType(RhsTLoc, RhsT,
6588+
diag::err_incomplete_type_used_in_type_trait_expr))
6589+
return false;
65816590

6582-
// Unions are never base classes, and never have base classes.
6583-
// It doesn't matter if they are complete or not. See PR#41843
6584-
if (lhsRecord && lhsRecord->getDecl()->isUnion())
6585-
return false;
6586-
if (rhsRecord && rhsRecord->getDecl()->isUnion())
6587-
return false;
6591+
return cast<CXXRecordDecl>(rhsRecord->getDecl())
6592+
->isDerivedFrom(cast<CXXRecordDecl>(lhsRecord->getDecl()));
6593+
}
65886594

6589-
if (lhsRecord == rhsRecord)
6590-
return true;
6595+
static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT, const TypeSourceInfo *Lhs,
6596+
const TypeSourceInfo *Rhs, SourceLocation KeyLoc) {
6597+
QualType LhsT = Lhs->getType();
6598+
QualType RhsT = Rhs->getType();
65916599

6592-
// C++0x [meta.rel]p2:
6593-
// If Base and Derived are class types and are different types
6594-
// (ignoring possible cv-qualifiers) then Derived shall be a
6595-
// complete type.
6596-
if (Self.RequireCompleteType(
6597-
Rhs->getTypeLoc().getBeginLoc(), RhsT,
6598-
diag::err_incomplete_type_used_in_type_trait_expr))
6599-
return false;
6600+
assert(!LhsT->isDependentType() && !RhsT->isDependentType() &&
6601+
"Cannot evaluate traits of dependent types");
6602+
6603+
switch(BTT) {
6604+
case BTT_IsBaseOf:
6605+
return Self.BuiltinIsBaseOf(Rhs->getTypeLoc().getBeginLoc(), LhsT, RhsT);
66006606

6601-
return cast<CXXRecordDecl>(rhsRecord->getDecl())
6602-
->isDerivedFrom(cast<CXXRecordDecl>(lhsRecord->getDecl()));
6603-
}
66046607
case BTT_IsVirtualBaseOf: {
66056608
const RecordType *BaseRecord = LhsT->getAs<RecordType>();
66066609
const RecordType *DerivedRecord = RhsT->getAs<RecordType>();
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// RUN: %clang_cc1 -triple=x86_64-linux-gnu -emit-llvm -o - %s | FileCheck %s
2+
3+
extern "C" void* memcpy(void*, const void*, decltype(sizeof(int)));
4+
void func();
5+
6+
namespace std {
7+
template <class T>
8+
class reference_wrapper {
9+
T* ptr;
10+
11+
public:
12+
T& get() { return *ptr; }
13+
};
14+
} // namespace std
15+
16+
struct Callable {
17+
void operator()() {}
18+
19+
void func();
20+
};
21+
22+
extern "C" void call1() {
23+
__builtin_invoke(func);
24+
__builtin_invoke(Callable{});
25+
__builtin_invoke(memcpy, nullptr, nullptr, 0);
26+
27+
// CHECK: define dso_local void @call1
28+
// CHECK-NEXT: entry:
29+
// CHECK-NEXT: %ref.tmp = alloca %struct.Callable, align 1
30+
// CHECK-NEXT: call void @_Z4funcv()
31+
// CHECK-NEXT: call void @_ZN8CallableclEv(ptr noundef nonnull align 1 dereferenceable(1) %ref.tmp)
32+
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 1 null, ptr align 1 null, i64 0, i1 false)
33+
// CHECK-NEXT: ret void
34+
}
35+
36+
extern "C" void call_memptr(std::reference_wrapper<Callable> wrapper) {
37+
__builtin_invoke(&Callable::func, wrapper);
38+
39+
// CHECK: define dso_local void @call_memptr
40+
// CHECK-NEXT: entry:
41+
// CHECK-NEXT: %wrapper = alloca %"class.std::reference_wrapper", align 8
42+
// CHECK-NEXT: %coerce.dive = getelementptr inbounds nuw %"class.std::reference_wrapper", ptr %wrapper, i32 0, i32 0
43+
// CHECK-NEXT: store ptr %wrapper.coerce, ptr %coerce.dive, align 8
44+
// CHECK-NEXT: %call = call noundef nonnull align 1 dereferenceable(1) ptr @_ZNSt17reference_wrapperI8CallableE3getEv(ptr noundef nonnull align 8 dereferenceable(8) %wrapper)
45+
// CHECK-NEXT: %0 = getelementptr inbounds i8, ptr %call, i64 0
46+
// CHECK-NEXT: br i1 false, label %memptr.virtual, label %memptr.nonvirtual
47+
// CHECK-EMPTY:
48+
// CHECK-NEXT: memptr.virtual:
49+
// CHECK-NEXT: %vtable = load ptr, ptr %0, align 8
50+
// CHECK-NEXT: %1 = getelementptr i8, ptr %vtable, i64 sub (i64 ptrtoint (ptr @_ZN8Callable4funcEv to i64), i64 1), !nosanitize !2
51+
// CHECK-NEXT: %memptr.virtualfn = load ptr, ptr %1, align 8, !nosanitize !2
52+
// CHECK-NEXT: br label %memptr.end
53+
// CHECK-EMPTY:
54+
// CHECK-NEXT: memptr.nonvirtual:
55+
// CHECK-NEXT: br label %memptr.end
56+
// CHECK-EMPTY:
57+
// CHECK-NEXT: memptr.end:
58+
// CHECK-NEXT: %2 = phi ptr [ %memptr.virtualfn, %memptr.virtual ], [ @_ZN8Callable4funcEv, %memptr.nonvirtual ]
59+
// CHECK-NEXT: call void %2(ptr noundef nonnull align 1 dereferenceable(1) %0)
60+
// CHECK-NEXT: ret void
61+
}

0 commit comments

Comments
 (0)