Skip to content

Commit 8d983bc

Browse files
committed
[Clang] Add __builtin_invoke and recognize std::invoke as a builtin
1 parent 6e57326 commit 8d983bc

File tree

8 files changed

+493
-74
lines changed

8 files changed

+493
-74
lines changed

clang/include/clang/Basic/Builtins.td

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

4245+
def Invoke : Builtin {
4246+
let Spellings = ["__builtin_invoke"];
4247+
let Attributes = [CustomTypeChecking, Constexpr];
4248+
let Prototype = "void(...)";
4249+
}
4250+
42454251
def Annotation : Builtin {
42464252
let Spellings = ["__builtin_annotation"];
42474253
let Attributes = [NoThrow, CustomTypeChecking];

clang/include/clang/Sema/Sema.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2206,6 +2206,8 @@ class Sema final : public SemaBase {
22062206
SourceLocation BuiltinLoc,
22072207
SourceLocation RParenLoc);
22082208

2209+
ExprResult BuiltinInvoke(CallExpr *TheCall);
2210+
22092211
enum FormatStringType {
22102212
FST_Scanf,
22112213
FST_Printf,
@@ -15030,11 +15032,18 @@ class Sema final : public SemaBase {
1503015032
SourceLocation Loc);
1503115033
QualType BuiltinRemoveReference(QualType BaseType, UTTKind UKind,
1503215034
SourceLocation Loc);
15035+
15036+
QualType BuiltinRemoveCVRef(QualType BaseType, SourceLocation Loc) {
15037+
return BuiltinRemoveReference(BaseType, UTTKind::RemoveCVRef, Loc);
15038+
}
15039+
1503315040
QualType BuiltinChangeCVRQualifiers(QualType BaseType, UTTKind UKind,
1503415041
SourceLocation Loc);
1503515042
QualType BuiltinChangeSignedness(QualType BaseType, UTTKind UKind,
1503615043
SourceLocation Loc);
1503715044

15045+
bool BuiltinIsBaseOf(SourceLocation RhsTLoc, QualType LhsT, QualType RhsT);
15046+
1503815047
/// Ensure that the type T is a literal type.
1503915048
///
1504015049
/// 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
@@ -2252,6 +2252,8 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
22522252
return BuiltinShuffleVector(TheCall);
22532253
// TheCall will be freed by the smart pointer here, but that's fine, since
22542254
// BuiltinShuffleVector guts it, but then doesn't release it.
2255+
case Builtin::BI__builtin_invoke:
2256+
return BuiltinInvoke(TheCall);
22552257
case Builtin::BI__builtin_prefetch:
22562258
if (BuiltinPrefetch(TheCall))
22572259
return ExprError();
@@ -5287,6 +5289,101 @@ ExprResult Sema::ConvertVectorExpr(Expr *E, TypeSourceInfo *TInfo,
52875289
RParenLoc, CurFPFeatureOverrides());
52885290
}
52895291

5292+
ExprResult Sema::BuiltinInvoke(CallExpr *TheCall) {
5293+
auto Loc = TheCall->getBeginLoc();
5294+
auto Args = MutableArrayRef(TheCall->getArgs(), TheCall->getNumArgs());
5295+
assert(llvm::none_of(Args,
5296+
[](Expr *Arg) { return Arg->isTypeDependent(); }));
5297+
5298+
if (Args.size() == 0) {
5299+
Diag(TheCall->getBeginLoc(), diag::err_typecheck_call_too_few_args_at_least)
5300+
<< 0 << 1 << 0 << 0 << TheCall->getSourceRange();
5301+
return ExprError();
5302+
}
5303+
5304+
auto FuncT = Args[0]->getType();
5305+
5306+
if (auto *MPT = FuncT->getAs<MemberPointerType>()) {
5307+
if (Args.size() < 2) {
5308+
Diag(TheCall->getBeginLoc(),
5309+
diag::err_typecheck_call_too_few_args_at_least)
5310+
<< 0 << 2 << 1 << 0 << TheCall->getSourceRange();
5311+
return ExprError();
5312+
}
5313+
5314+
auto *MemPtrClass = MPT->getClass();
5315+
auto ObjectT = Args[1]->getType();
5316+
5317+
5318+
if (MPT->isMemberDataPointer() && Args.size() != 2) {
5319+
Diag(TheCall->getBeginLoc(), diag::err_typecheck_call_too_many_args)
5320+
<< 0 << 2 << Args.size() << 0 << TheCall->getSourceRange();
5321+
return ExprError();
5322+
}
5323+
5324+
ExprResult ObjectArg = [&]() -> ExprResult {
5325+
// (1.1): (t1.*f)(t2, …, tN) when f is a pointer to a member function of a
5326+
// class T and is_same_v<T, remove_cvref_t<decltype(t1)>> ||
5327+
// is_base_of_v<T, remove_cvref_t<decltype(t1)>> is true;
5328+
// (1.4): t1.*f when N=1 and f is a pointer to data member of a class T
5329+
// and is_same_v<T, remove_cvref_t<decltype(t1)>> ||
5330+
// is_base_of_v<T, remove_cvref_t<decltype(t1)>> is true;
5331+
if (Context.hasSameType(QualType(MemPtrClass, 0),
5332+
BuiltinRemoveCVRef(ObjectT, Loc)) ||
5333+
BuiltinIsBaseOf(Args[1]->getBeginLoc(), QualType(MemPtrClass, 0),
5334+
BuiltinRemoveCVRef(ObjectT, Loc))) {
5335+
return Args[1];
5336+
}
5337+
5338+
// (t1.get().*f)(t2, …, tN) when f is a pointer to a member function of
5339+
// a class T and remove_cvref_t<decltype(t1)> is a specialization of
5340+
// reference_wrapper;
5341+
if (auto *RD = ObjectT->getAsCXXRecordDecl()) {
5342+
if (RD->isInStdNamespace() &&
5343+
RD->getDeclName().getAsString() == "reference_wrapper") {
5344+
CXXScopeSpec SS;
5345+
IdentifierInfo *GetName = &Context.Idents.get("get");
5346+
UnqualifiedId GetID;
5347+
GetID.setIdentifier(GetName, Loc);
5348+
5349+
auto MemExpr = ActOnMemberAccessExpr(
5350+
getCurScope(), Args[1], Loc, tok::period, SS,
5351+
/*TemplateKWLoc=*/SourceLocation(), GetID, nullptr);
5352+
5353+
if (MemExpr.isInvalid())
5354+
return ExprError();
5355+
5356+
return ActOnCallExpr(getCurScope(), MemExpr.get(), Loc, {}, Loc);
5357+
}
5358+
}
5359+
5360+
// ((*t1).*f)(t2, …, tN) when f is a pointer to a member function of a
5361+
// class T and t1 does not satisfy the previous two items;
5362+
5363+
return ActOnUnaryOp(getCurScope(), Loc, tok::star, Args[1]);
5364+
}();
5365+
5366+
if (ObjectArg.isInvalid())
5367+
return ExprError();
5368+
5369+
auto BinOp = ActOnBinOp(getCurScope(), TheCall->getBeginLoc(),
5370+
tok::periodstar, ObjectArg.get(), Args[0]);
5371+
if (BinOp.isInvalid())
5372+
return ExprError();
5373+
5374+
if (MPT->isMemberDataPointer())
5375+
return BinOp;
5376+
5377+
auto *MemCall = new (Context)
5378+
ParenExpr(SourceLocation(), SourceLocation(), BinOp.get());
5379+
5380+
return ActOnCallExpr(getCurScope(), MemCall, TheCall->getBeginLoc(),
5381+
Args.drop_front(2), TheCall->getRParenLoc());
5382+
}
5383+
return ActOnCallExpr(getCurScope(), Args.front(), TheCall->getBeginLoc(),
5384+
Args.drop_front(), TheCall->getRParenLoc());
5385+
}
5386+
52905387
bool Sema::BuiltinPrefetch(CallExpr *TheCall) {
52915388
unsigned NumArgs = TheCall->getNumArgs();
52925389

clang/lib/Sema/SemaExprCXX.cpp

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

6072-
static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT, const TypeSourceInfo *Lhs,
6073-
const TypeSourceInfo *Rhs, SourceLocation KeyLoc) {
6074-
QualType LhsT = Lhs->getType();
6075-
QualType RhsT = Rhs->getType();
6072+
bool Sema::BuiltinIsBaseOf(SourceLocation RhsTLoc, QualType LhsT,
6073+
QualType RhsT) {
6074+
// C++0x [meta.rel]p2
6075+
// Base is a base class of Derived without regard to cv-qualifiers or
6076+
// Base and Derived are not unions and name the same class type without
6077+
// regard to cv-qualifiers.
6078+
6079+
const RecordType *lhsRecord = LhsT->getAs<RecordType>();
6080+
const RecordType *rhsRecord = RhsT->getAs<RecordType>();
6081+
if (!rhsRecord || !lhsRecord) {
6082+
const ObjCObjectType *LHSObjTy = LhsT->getAs<ObjCObjectType>();
6083+
const ObjCObjectType *RHSObjTy = RhsT->getAs<ObjCObjectType>();
6084+
if (!LHSObjTy || !RHSObjTy)
6085+
return false;
60766086

6077-
assert(!LhsT->isDependentType() && !RhsT->isDependentType() &&
6078-
"Cannot evaluate traits of dependent types");
6087+
ObjCInterfaceDecl *BaseInterface = LHSObjTy->getInterface();
6088+
ObjCInterfaceDecl *DerivedInterface = RHSObjTy->getInterface();
6089+
if (!BaseInterface || !DerivedInterface)
6090+
return false;
60796091

6080-
switch(BTT) {
6081-
case BTT_IsBaseOf: {
6082-
// C++0x [meta.rel]p2
6083-
// Base is a base class of Derived without regard to cv-qualifiers or
6084-
// Base and Derived are not unions and name the same class type without
6085-
// regard to cv-qualifiers.
6086-
6087-
const RecordType *lhsRecord = LhsT->getAs<RecordType>();
6088-
const RecordType *rhsRecord = RhsT->getAs<RecordType>();
6089-
if (!rhsRecord || !lhsRecord) {
6090-
const ObjCObjectType *LHSObjTy = LhsT->getAs<ObjCObjectType>();
6091-
const ObjCObjectType *RHSObjTy = RhsT->getAs<ObjCObjectType>();
6092-
if (!LHSObjTy || !RHSObjTy)
6093-
return false;
6092+
if (RequireCompleteType(RhsTLoc, RhsT,
6093+
diag::err_incomplete_type_used_in_type_trait_expr))
6094+
return false;
60946095

6095-
ObjCInterfaceDecl *BaseInterface = LHSObjTy->getInterface();
6096-
ObjCInterfaceDecl *DerivedInterface = RHSObjTy->getInterface();
6097-
if (!BaseInterface || !DerivedInterface)
6098-
return false;
6096+
return BaseInterface->isSuperClassOf(DerivedInterface);
6097+
}
60996098

6100-
if (Self.RequireCompleteType(
6101-
Rhs->getTypeLoc().getBeginLoc(), RhsT,
6102-
diag::err_incomplete_type_used_in_type_trait_expr))
6103-
return false;
6099+
assert(Context.hasSameUnqualifiedType(LhsT, RhsT) ==
6100+
(lhsRecord == rhsRecord));
61046101

6105-
return BaseInterface->isSuperClassOf(DerivedInterface);
6106-
}
6102+
// Unions are never base classes, and never have base classes.
6103+
// It doesn't matter if they are complete or not. See PR#41843
6104+
if (lhsRecord && lhsRecord->getDecl()->isUnion())
6105+
return false;
6106+
if (rhsRecord && rhsRecord->getDecl()->isUnion())
6107+
return false;
6108+
6109+
if (lhsRecord == rhsRecord)
6110+
return true;
61076111

6108-
assert(Self.Context.hasSameUnqualifiedType(LhsT, RhsT)
6109-
== (lhsRecord == rhsRecord));
6112+
// C++0x [meta.rel]p2:
6113+
// If Base and Derived are class types and are different types
6114+
// (ignoring possible cv-qualifiers) then Derived shall be a
6115+
// complete type.
6116+
if (RequireCompleteType(RhsTLoc, RhsT,
6117+
diag::err_incomplete_type_used_in_type_trait_expr))
6118+
return false;
61106119

6111-
// Unions are never base classes, and never have base classes.
6112-
// It doesn't matter if they are complete or not. See PR#41843
6113-
if (lhsRecord && lhsRecord->getDecl()->isUnion())
6114-
return false;
6115-
if (rhsRecord && rhsRecord->getDecl()->isUnion())
6116-
return false;
6120+
return cast<CXXRecordDecl>(rhsRecord->getDecl())
6121+
->isDerivedFrom(cast<CXXRecordDecl>(lhsRecord->getDecl()));
6122+
}
61176123

6118-
if (lhsRecord == rhsRecord)
6119-
return true;
6124+
static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT, const TypeSourceInfo *Lhs,
6125+
const TypeSourceInfo *Rhs, SourceLocation KeyLoc) {
6126+
QualType LhsT = Lhs->getType();
6127+
QualType RhsT = Rhs->getType();
61206128

6121-
// C++0x [meta.rel]p2:
6122-
// If Base and Derived are class types and are different types
6123-
// (ignoring possible cv-qualifiers) then Derived shall be a
6124-
// complete type.
6125-
if (Self.RequireCompleteType(
6126-
Rhs->getTypeLoc().getBeginLoc(), RhsT,
6127-
diag::err_incomplete_type_used_in_type_trait_expr))
6128-
return false;
6129+
assert(!LhsT->isDependentType() && !RhsT->isDependentType() &&
6130+
"Cannot evaluate traits of dependent types");
6131+
6132+
switch(BTT) {
6133+
case BTT_IsBaseOf:
6134+
return Self.BuiltinIsBaseOf(Rhs->getTypeLoc().getBeginLoc(), LhsT, RhsT);
61296135

6130-
return cast<CXXRecordDecl>(rhsRecord->getDecl())
6131-
->isDerivedFrom(cast<CXXRecordDecl>(lhsRecord->getDecl()));
6132-
}
61336136
case BTT_IsVirtualBaseOf: {
61346137
const RecordType *BaseRecord = LhsT->getAs<RecordType>();
61356138
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)