Skip to content

Commit bff4c44

Browse files
committed
[Clang] Add __builtin_invoke and recognize std::invoke as a builtin
1 parent 025f937 commit bff4c44

File tree

8 files changed

+494
-80
lines changed

8 files changed

+494
-80
lines changed

clang/include/clang/Basic/Builtins.td

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

4212+
def Invoke : Builtin {
4213+
let Spellings = ["__builtin_invoke"];
4214+
let Attributes = [CustomTypeChecking, Constexpr];
4215+
let Prototype = "void(...)";
4216+
}
4217+
42124218
def Annotation : Builtin {
42134219
let Spellings = ["__builtin_annotation"];
42144220
let Attributes = [NoThrow, CustomTypeChecking];

clang/include/clang/Sema/Sema.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2175,6 +2175,8 @@ class Sema final : public SemaBase {
21752175
SourceLocation BuiltinLoc,
21762176
SourceLocation RParenLoc);
21772177

2178+
ExprResult BuiltinInvoke(CallExpr *TheCall);
2179+
21782180
enum FormatStringType {
21792181
FST_Scanf,
21802182
FST_Printf,
@@ -14879,11 +14881,18 @@ class Sema final : public SemaBase {
1487914881
SourceLocation Loc);
1488014882
QualType BuiltinRemoveReference(QualType BaseType, UTTKind UKind,
1488114883
SourceLocation Loc);
14884+
14885+
QualType BuiltinRemoveCVRef(QualType BaseType, SourceLocation Loc) {
14886+
return BuiltinRemoveReference(BaseType, UTTKind::RemoveCVRef, Loc);
14887+
}
14888+
1488214889
QualType BuiltinChangeCVRQualifiers(QualType BaseType, UTTKind UKind,
1488314890
SourceLocation Loc);
1488414891
QualType BuiltinChangeSignedness(QualType BaseType, UTTKind UKind,
1488514892
SourceLocation Loc);
1488614893

14894+
bool BuiltinIsBaseOf(SourceLocation RhsTLoc, QualType LhsT, QualType RhsT);
14895+
1488714896
/// Ensure that the type T is a literal type.
1488814897
///
1488914898
/// This routine checks whether the type @p T is a literal type. If @p T is an

clang/lib/Sema/SemaChecking.cpp

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2246,6 +2246,8 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
22462246
return BuiltinShuffleVector(TheCall);
22472247
// TheCall will be freed by the smart pointer here, but that's fine, since
22482248
// BuiltinShuffleVector guts it, but then doesn't release it.
2249+
case Builtin::BI__builtin_invoke:
2250+
return BuiltinInvoke(TheCall);
22492251
case Builtin::BI__builtin_prefetch:
22502252
if (BuiltinPrefetch(TheCall))
22512253
return ExprError();
@@ -5178,6 +5180,101 @@ ExprResult Sema::ConvertVectorExpr(Expr *E, TypeSourceInfo *TInfo,
51785180
BuiltinLoc, RParenLoc);
51795181
}
51805182

5183+
ExprResult Sema::BuiltinInvoke(CallExpr *TheCall) {
5184+
auto Loc = TheCall->getBeginLoc();
5185+
auto Args = MutableArrayRef(TheCall->getArgs(), TheCall->getNumArgs());
5186+
assert(llvm::none_of(Args,
5187+
[](Expr *Arg) { return Arg->isTypeDependent(); }));
5188+
5189+
if (Args.size() == 0) {
5190+
Diag(TheCall->getBeginLoc(), diag::err_typecheck_call_too_few_args_at_least)
5191+
<< 0 << 1 << 0 << 0 << TheCall->getSourceRange();
5192+
return ExprError();
5193+
}
5194+
5195+
auto FuncT = Args[0]->getType();
5196+
5197+
if (auto *MPT = FuncT->getAs<MemberPointerType>()) {
5198+
if (Args.size() < 2) {
5199+
Diag(TheCall->getBeginLoc(),
5200+
diag::err_typecheck_call_too_few_args_at_least)
5201+
<< 0 << 2 << 1 << 0 << TheCall->getSourceRange();
5202+
return ExprError();
5203+
}
5204+
5205+
auto *MemPtrClass = MPT->getClass();
5206+
auto ObjectT = Args[1]->getType();
5207+
5208+
5209+
if (MPT->isMemberDataPointer() && Args.size() != 2) {
5210+
Diag(TheCall->getBeginLoc(), diag::err_typecheck_call_too_many_args)
5211+
<< 0 << 2 << Args.size() << 0 << TheCall->getSourceRange();
5212+
return ExprError();
5213+
}
5214+
5215+
ExprResult ObjectArg = [&]() -> ExprResult {
5216+
// (1.1): (t1.*f)(t2, …, tN) when f is a pointer to a member function of a
5217+
// class T and is_same_v<T, remove_cvref_t<decltype(t1)>> ||
5218+
// is_base_of_v<T, remove_cvref_t<decltype(t1)>> is true;
5219+
// (1.4): t1.*f when N=1 and f is a pointer to data member of a class T
5220+
// and is_same_v<T, remove_cvref_t<decltype(t1)>> ||
5221+
// is_base_of_v<T, remove_cvref_t<decltype(t1)>> is true;
5222+
if (Context.hasSameType(QualType(MemPtrClass, 0),
5223+
BuiltinRemoveCVRef(ObjectT, Loc)) ||
5224+
BuiltinIsBaseOf(Args[1]->getBeginLoc(), QualType(MemPtrClass, 0),
5225+
BuiltinRemoveCVRef(ObjectT, Loc))) {
5226+
return Args[1];
5227+
}
5228+
5229+
// (t1.get().*f)(t2, …, tN) when f is a pointer to a member function of
5230+
// a class T and remove_cvref_t<decltype(t1)> is a specialization of
5231+
// reference_wrapper;
5232+
if (auto *RD = ObjectT->getAsCXXRecordDecl()) {
5233+
if (RD->isInStdNamespace() &&
5234+
RD->getDeclName().getAsString() == "reference_wrapper") {
5235+
CXXScopeSpec SS;
5236+
IdentifierInfo *GetName = &Context.Idents.get("get");
5237+
UnqualifiedId GetID;
5238+
GetID.setIdentifier(GetName, Loc);
5239+
5240+
auto MemExpr = ActOnMemberAccessExpr(
5241+
getCurScope(), Args[1], Loc, tok::period, SS,
5242+
/*TemplateKWLoc=*/SourceLocation(), GetID, nullptr);
5243+
5244+
if (MemExpr.isInvalid())
5245+
return ExprError();
5246+
5247+
return ActOnCallExpr(getCurScope(), MemExpr.get(), Loc, {}, Loc);
5248+
}
5249+
}
5250+
5251+
// ((*t1).*f)(t2, …, tN) when f is a pointer to a member function of a
5252+
// class T and t1 does not satisfy the previous two items;
5253+
5254+
return ActOnUnaryOp(getCurScope(), Loc, tok::star, Args[1]);
5255+
}();
5256+
5257+
if (ObjectArg.isInvalid())
5258+
return ExprError();
5259+
5260+
auto BinOp = ActOnBinOp(getCurScope(), TheCall->getBeginLoc(),
5261+
tok::periodstar, ObjectArg.get(), Args[0]);
5262+
if (BinOp.isInvalid())
5263+
return ExprError();
5264+
5265+
if (MPT->isMemberDataPointer())
5266+
return BinOp;
5267+
5268+
auto *MemCall = new (Context)
5269+
ParenExpr(SourceLocation(), SourceLocation(), BinOp.get());
5270+
5271+
return ActOnCallExpr(getCurScope(), MemCall, TheCall->getBeginLoc(),
5272+
Args.drop_front(2), TheCall->getRParenLoc());
5273+
}
5274+
return ActOnCallExpr(getCurScope(), Args.front(), TheCall->getBeginLoc(),
5275+
Args.drop_front(), TheCall->getRParenLoc());
5276+
}
5277+
51815278
bool Sema::BuiltinPrefetch(CallExpr *TheCall) {
51825279
unsigned NumArgs = TheCall->getNumArgs();
51835280

clang/lib/Sema/SemaExprCXX.cpp

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

6060-
static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT, const TypeSourceInfo *Lhs,
6061-
const TypeSourceInfo *Rhs, SourceLocation KeyLoc) {
6062-
QualType LhsT = Lhs->getType();
6063-
QualType RhsT = Rhs->getType();
6060+
bool Sema::BuiltinIsBaseOf(SourceLocation RhsTLoc, QualType LhsT,
6061+
QualType RhsT) {
6062+
// C++0x [meta.rel]p2
6063+
// Base is a base class of Derived without regard to cv-qualifiers or
6064+
// Base and Derived are not unions and name the same class type without
6065+
// regard to cv-qualifiers.
6066+
6067+
const RecordType *lhsRecord = LhsT->getAs<RecordType>();
6068+
const RecordType *rhsRecord = RhsT->getAs<RecordType>();
6069+
if (!rhsRecord || !lhsRecord) {
6070+
const ObjCObjectType *LHSObjTy = LhsT->getAs<ObjCObjectType>();
6071+
const ObjCObjectType *RHSObjTy = RhsT->getAs<ObjCObjectType>();
6072+
if (!LHSObjTy || !RHSObjTy)
6073+
return false;
60646074

6065-
assert(!LhsT->isDependentType() && !RhsT->isDependentType() &&
6066-
"Cannot evaluate traits of dependent types");
6075+
ObjCInterfaceDecl *BaseInterface = LHSObjTy->getInterface();
6076+
ObjCInterfaceDecl *DerivedInterface = RHSObjTy->getInterface();
6077+
if (!BaseInterface || !DerivedInterface)
6078+
return false;
60676079

6068-
switch(BTT) {
6069-
case BTT_IsBaseOf: {
6070-
// C++0x [meta.rel]p2
6071-
// Base is a base class of Derived without regard to cv-qualifiers or
6072-
// Base and Derived are not unions and name the same class type without
6073-
// regard to cv-qualifiers.
6074-
6075-
const RecordType *lhsRecord = LhsT->getAs<RecordType>();
6076-
const RecordType *rhsRecord = RhsT->getAs<RecordType>();
6077-
if (!rhsRecord || !lhsRecord) {
6078-
const ObjCObjectType *LHSObjTy = LhsT->getAs<ObjCObjectType>();
6079-
const ObjCObjectType *RHSObjTy = RhsT->getAs<ObjCObjectType>();
6080-
if (!LHSObjTy || !RHSObjTy)
6081-
return false;
6080+
if (RequireCompleteType(RhsTLoc, RhsT,
6081+
diag::err_incomplete_type_used_in_type_trait_expr))
6082+
return false;
60826083

6083-
ObjCInterfaceDecl *BaseInterface = LHSObjTy->getInterface();
6084-
ObjCInterfaceDecl *DerivedInterface = RHSObjTy->getInterface();
6085-
if (!BaseInterface || !DerivedInterface)
6086-
return false;
6084+
return BaseInterface->isSuperClassOf(DerivedInterface);
6085+
}
60876086

6088-
if (Self.RequireCompleteType(
6089-
Rhs->getTypeLoc().getBeginLoc(), RhsT,
6090-
diag::err_incomplete_type_used_in_type_trait_expr))
6091-
return false;
6087+
assert(Context.hasSameUnqualifiedType(LhsT, RhsT) ==
6088+
(lhsRecord == rhsRecord));
60926089

6093-
return BaseInterface->isSuperClassOf(DerivedInterface);
6094-
}
6090+
// Unions are never base classes, and never have base classes.
6091+
// It doesn't matter if they are complete or not. See PR#41843
6092+
if (lhsRecord && lhsRecord->getDecl()->isUnion())
6093+
return false;
6094+
if (rhsRecord && rhsRecord->getDecl()->isUnion())
6095+
return false;
6096+
6097+
if (lhsRecord == rhsRecord)
6098+
return true;
60956099

6096-
assert(Self.Context.hasSameUnqualifiedType(LhsT, RhsT)
6097-
== (lhsRecord == rhsRecord));
6100+
// C++0x [meta.rel]p2:
6101+
// If Base and Derived are class types and are different types
6102+
// (ignoring possible cv-qualifiers) then Derived shall be a
6103+
// complete type.
6104+
if (RequireCompleteType(RhsTLoc, RhsT,
6105+
diag::err_incomplete_type_used_in_type_trait_expr))
6106+
return false;
60986107

6099-
// Unions are never base classes, and never have base classes.
6100-
// It doesn't matter if they are complete or not. See PR#41843
6101-
if (lhsRecord && lhsRecord->getDecl()->isUnion())
6102-
return false;
6103-
if (rhsRecord && rhsRecord->getDecl()->isUnion())
6104-
return false;
6108+
return cast<CXXRecordDecl>(rhsRecord->getDecl())
6109+
->isDerivedFrom(cast<CXXRecordDecl>(lhsRecord->getDecl()));
6110+
}
61056111

6106-
if (lhsRecord == rhsRecord)
6107-
return true;
6112+
static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT, const TypeSourceInfo *Lhs,
6113+
const TypeSourceInfo *Rhs, SourceLocation KeyLoc) {
6114+
QualType LhsT = Lhs->getType();
6115+
QualType RhsT = Rhs->getType();
61086116

6109-
// C++0x [meta.rel]p2:
6110-
// If Base and Derived are class types and are different types
6111-
// (ignoring possible cv-qualifiers) then Derived shall be a
6112-
// complete type.
6113-
if (Self.RequireCompleteType(
6114-
Rhs->getTypeLoc().getBeginLoc(), RhsT,
6115-
diag::err_incomplete_type_used_in_type_trait_expr))
6116-
return false;
6117+
assert(!LhsT->isDependentType() && !RhsT->isDependentType() &&
6118+
"Cannot evaluate traits of dependent types");
6119+
6120+
switch(BTT) {
6121+
case BTT_IsBaseOf:
6122+
return Self.BuiltinIsBaseOf(Rhs->getTypeLoc().getBeginLoc(), LhsT, RhsT);
61176123

6118-
return cast<CXXRecordDecl>(rhsRecord->getDecl())
6119-
->isDerivedFrom(cast<CXXRecordDecl>(lhsRecord->getDecl()));
6120-
}
61216124
case BTT_IsVirtualBaseOf: {
61226125
const RecordType *BaseRecord = LhsT->getAs<RecordType>();
61236126
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)