Skip to content

Commit ddcc918

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

File tree

8 files changed

+493
-79
lines changed

8 files changed

+493
-79
lines changed

clang/include/clang/Basic/Builtins.td

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

4257+
def Invoke : Builtin {
4258+
let Spellings = ["__builtin_invoke"];
4259+
let Attributes = [CustomTypeChecking, Constexpr];
4260+
let Prototype = "void(...)";
4261+
}
4262+
42574263
def Annotation : Builtin {
42584264
let Spellings = ["__builtin_annotation"];
42594265
let Attributes = [NoThrow, CustomTypeChecking];

clang/include/clang/Sema/Sema.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2212,6 +2212,8 @@ class Sema final : public SemaBase {
22122212
SourceLocation BuiltinLoc,
22132213
SourceLocation RParenLoc);
22142214

2215+
ExprResult BuiltinInvoke(CallExpr *TheCall);
2216+
22152217
enum FormatStringType {
22162218
FST_Scanf,
22172219
FST_Printf,
@@ -15080,11 +15082,18 @@ class Sema final : public SemaBase {
1508015082
SourceLocation Loc);
1508115083
QualType BuiltinRemoveReference(QualType BaseType, UTTKind UKind,
1508215084
SourceLocation Loc);
15085+
15086+
QualType BuiltinRemoveCVRef(QualType BaseType, SourceLocation Loc) {
15087+
return BuiltinRemoveReference(BaseType, UTTKind::RemoveCVRef, Loc);
15088+
}
15089+
1508315090
QualType BuiltinChangeCVRQualifiers(QualType BaseType, UTTKind UKind,
1508415091
SourceLocation Loc);
1508515092
QualType BuiltinChangeSignedness(QualType BaseType, UTTKind UKind,
1508615093
SourceLocation Loc);
1508715094

15095+
bool BuiltinIsBaseOf(SourceLocation RhsTLoc, QualType LhsT, QualType RhsT);
15096+
1508815097
/// Ensure that the type T is a literal type.
1508915098
///
1509015099
/// 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
@@ -2273,6 +2273,8 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
22732273
return BuiltinShuffleVector(TheCall);
22742274
// TheCall will be freed by the smart pointer here, but that's fine, since
22752275
// BuiltinShuffleVector guts it, but then doesn't release it.
2276+
case Builtin::BI__builtin_invoke:
2277+
return BuiltinInvoke(TheCall);
22762278
case Builtin::BI__builtin_prefetch:
22772279
if (BuiltinPrefetch(TheCall))
22782280
return ExprError();
@@ -5290,6 +5292,101 @@ ExprResult Sema::ConvertVectorExpr(Expr *E, TypeSourceInfo *TInfo,
52905292
RParenLoc, CurFPFeatureOverrides());
52915293
}
52925294

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

clang/lib/Sema/SemaExprCXX.cpp

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

6105-
static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT, const TypeSourceInfo *Lhs,
6106-
const TypeSourceInfo *Rhs, SourceLocation KeyLoc) {
6107-
QualType LhsT = Lhs->getType();
6108-
QualType RhsT = Rhs->getType();
6105+
bool Sema::BuiltinIsBaseOf(SourceLocation RhsTLoc, QualType LhsT,
6106+
QualType RhsT) {
6107+
// C++0x [meta.rel]p2
6108+
// Base is a base class of Derived without regard to cv-qualifiers or
6109+
// Base and Derived are not unions and name the same class type without
6110+
// regard to cv-qualifiers.
6111+
6112+
const RecordType *lhsRecord = LhsT->getAs<RecordType>();
6113+
const RecordType *rhsRecord = RhsT->getAs<RecordType>();
6114+
if (!rhsRecord || !lhsRecord) {
6115+
const ObjCObjectType *LHSObjTy = LhsT->getAs<ObjCObjectType>();
6116+
const ObjCObjectType *RHSObjTy = RhsT->getAs<ObjCObjectType>();
6117+
if (!LHSObjTy || !RHSObjTy)
6118+
return false;
61096119

6110-
assert(!LhsT->isDependentType() && !RhsT->isDependentType() &&
6111-
"Cannot evaluate traits of dependent types");
6120+
ObjCInterfaceDecl *BaseInterface = LHSObjTy->getInterface();
6121+
ObjCInterfaceDecl *DerivedInterface = RHSObjTy->getInterface();
6122+
if (!BaseInterface || !DerivedInterface)
6123+
return false;
61126124

6113-
switch(BTT) {
6114-
case BTT_IsBaseOf: {
6115-
// C++0x [meta.rel]p2
6116-
// Base is a base class of Derived without regard to cv-qualifiers or
6117-
// Base and Derived are not unions and name the same class type without
6118-
// regard to cv-qualifiers.
6119-
6120-
const RecordType *lhsRecord = LhsT->getAs<RecordType>();
6121-
const RecordType *rhsRecord = RhsT->getAs<RecordType>();
6122-
if (!rhsRecord || !lhsRecord) {
6123-
const ObjCObjectType *LHSObjTy = LhsT->getAs<ObjCObjectType>();
6124-
const ObjCObjectType *RHSObjTy = RhsT->getAs<ObjCObjectType>();
6125-
if (!LHSObjTy || !RHSObjTy)
6126-
return false;
6125+
if (RequireCompleteType(RhsTLoc, RhsT,
6126+
diag::err_incomplete_type_used_in_type_trait_expr))
6127+
return false;
61276128

6128-
ObjCInterfaceDecl *BaseInterface = LHSObjTy->getInterface();
6129-
ObjCInterfaceDecl *DerivedInterface = RHSObjTy->getInterface();
6130-
if (!BaseInterface || !DerivedInterface)
6131-
return false;
6129+
return BaseInterface->isSuperClassOf(DerivedInterface);
6130+
}
61326131

6133-
if (Self.RequireCompleteType(
6134-
Rhs->getTypeLoc().getBeginLoc(), RhsT,
6135-
diag::err_incomplete_type_used_in_type_trait_expr))
6136-
return false;
6132+
assert(Context.hasSameUnqualifiedType(LhsT, RhsT) ==
6133+
(lhsRecord == rhsRecord));
61376134

6138-
return BaseInterface->isSuperClassOf(DerivedInterface);
6139-
}
6135+
// Unions are never base classes, and never have base classes.
6136+
// It doesn't matter if they are complete or not. See PR#41843
6137+
if (lhsRecord && lhsRecord->getDecl()->isUnion())
6138+
return false;
6139+
if (rhsRecord && rhsRecord->getDecl()->isUnion())
6140+
return false;
6141+
6142+
if (lhsRecord == rhsRecord)
6143+
return true;
61406144

6141-
assert(Self.Context.hasSameUnqualifiedType(LhsT, RhsT)
6142-
== (lhsRecord == rhsRecord));
6145+
// C++0x [meta.rel]p2:
6146+
// If Base and Derived are class types and are different types
6147+
// (ignoring possible cv-qualifiers) then Derived shall be a
6148+
// complete type.
6149+
if (RequireCompleteType(RhsTLoc, RhsT,
6150+
diag::err_incomplete_type_used_in_type_trait_expr))
6151+
return false;
61436152

6144-
// Unions are never base classes, and never have base classes.
6145-
// It doesn't matter if they are complete or not. See PR#41843
6146-
if (lhsRecord && lhsRecord->getDecl()->isUnion())
6147-
return false;
6148-
if (rhsRecord && rhsRecord->getDecl()->isUnion())
6149-
return false;
6153+
return cast<CXXRecordDecl>(rhsRecord->getDecl())
6154+
->isDerivedFrom(cast<CXXRecordDecl>(lhsRecord->getDecl()));
6155+
}
61506156

6151-
if (lhsRecord == rhsRecord)
6152-
return true;
6157+
static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT, const TypeSourceInfo *Lhs,
6158+
const TypeSourceInfo *Rhs, SourceLocation KeyLoc) {
6159+
QualType LhsT = Lhs->getType();
6160+
QualType RhsT = Rhs->getType();
61536161

6154-
// C++0x [meta.rel]p2:
6155-
// If Base and Derived are class types and are different types
6156-
// (ignoring possible cv-qualifiers) then Derived shall be a
6157-
// complete type.
6158-
if (Self.RequireCompleteType(
6159-
Rhs->getTypeLoc().getBeginLoc(), RhsT,
6160-
diag::err_incomplete_type_used_in_type_trait_expr))
6161-
return false;
6162+
assert(!LhsT->isDependentType() && !RhsT->isDependentType() &&
6163+
"Cannot evaluate traits of dependent types");
6164+
6165+
switch(BTT) {
6166+
case BTT_IsBaseOf:
6167+
return Self.BuiltinIsBaseOf(Rhs->getTypeLoc().getBeginLoc(), LhsT, RhsT);
61626168

6163-
return cast<CXXRecordDecl>(rhsRecord->getDecl())
6164-
->isDerivedFrom(cast<CXXRecordDecl>(lhsRecord->getDecl()));
6165-
}
61666169
case BTT_IsVirtualBaseOf: {
61676170
const RecordType *BaseRecord = LhsT->getAs<RecordType>();
61686171
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)