-
Notifications
You must be signed in to change notification settings - Fork 14.7k
[Clang] Add __builtin_invoke and use it in libc++ #116709
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
e0bb550
2849e45
fd17bcc
e66f728
24427c9
54ea97a
e8a0c87
d046919
4053449
9317801
abbb273
dfbe219
7539895
d4f582d
497c915
635d6ef
faffb4e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2261,6 +2261,99 @@ static bool BuiltinCountZeroBitsGeneric(Sema &S, CallExpr *TheCall) { | |
return false; | ||
} | ||
|
||
static ExprResult BuiltinInvoke(Sema &S, CallExpr *TheCall) { | ||
SourceLocation Loc = TheCall->getBeginLoc(); | ||
MutableArrayRef Args(TheCall->getArgs(), TheCall->getNumArgs()); | ||
assert(llvm::none_of(Args, [](Expr *Arg) { return Arg->isTypeDependent(); })); | ||
|
||
if (Args.size() == 0) { | ||
S.Diag(TheCall->getBeginLoc(), | ||
diag::err_typecheck_call_too_few_args_at_least) | ||
<< /*callee_type=*/0 << /*min_arg_count=*/1 << /*actual_arg_count=*/0 | ||
<< /*is_non_object=*/0 << TheCall->getSourceRange(); | ||
return ExprError(); | ||
} | ||
|
||
QualType FuncT = Args[0]->getType(); | ||
|
||
if (const auto *MPT = FuncT->getAs<MemberPointerType>()) { | ||
if (Args.size() < 2) { | ||
philnik777 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
S.Diag(TheCall->getBeginLoc(), | ||
diag::err_typecheck_call_too_few_args_at_least) | ||
<< /*callee_type=*/0 << /*min_arg_count=*/2 << /*actual_arg_count=*/1 | ||
<< /*is_non_object=*/0 << TheCall->getSourceRange(); | ||
return ExprError(); | ||
} | ||
|
||
const Type *MemPtrClass = MPT->getQualifier()->getAsType(); | ||
QualType ObjectT = Args[1]->getType(); | ||
|
||
if (MPT->isMemberDataPointer() && S.checkArgCount(TheCall, 2)) | ||
return ExprError(); | ||
|
||
ExprResult ObjectArg = [&]() -> ExprResult { | ||
// (1.1): (t1.*f)(t2, ..., tN) when f is a pointer to a member function of | ||
// a class T and is_same_v<T, remove_cvref_t<decltype(t1)>> || | ||
// is_base_of_v<T, remove_cvref_t<decltype(t1)>> is true; | ||
// (1.4): t1.*f when N=1 and f is a pointer to data member of a class T | ||
// and is_same_v<T, remove_cvref_t<decltype(t1)>> || | ||
// is_base_of_v<T, remove_cvref_t<decltype(t1)>> is true; | ||
if (S.Context.hasSameType(QualType(MemPtrClass, 0), | ||
S.BuiltinRemoveCVRef(ObjectT, Loc)) || | ||
S.BuiltinIsBaseOf(Args[1]->getBeginLoc(), QualType(MemPtrClass, 0), | ||
S.BuiltinRemoveCVRef(ObjectT, Loc))) { | ||
return Args[1]; | ||
} | ||
|
||
// (t1.get().*f)(t2, ..., tN) when f is a pointer to a member function of | ||
// a class T and remove_cvref_t<decltype(t1)> is a specialization of | ||
// reference_wrapper; | ||
if (const auto *RD = ObjectT->getAsCXXRecordDecl()) { | ||
if (RD->isInStdNamespace() && | ||
RD->getDeclName().getAsString() == "reference_wrapper") { | ||
Comment on lines
+2312
to
+2313
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not opposed, but I'm also not delighted for more special knowledge about STL interfaces leaking into the compiler implementation. |
||
CXXScopeSpec SS; | ||
IdentifierInfo *GetName = &S.Context.Idents.get("get"); | ||
UnqualifiedId GetID; | ||
GetID.setIdentifier(GetName, Loc); | ||
|
||
ExprResult MemExpr = S.ActOnMemberAccessExpr( | ||
S.getCurScope(), Args[1], Loc, tok::period, SS, | ||
/*TemplateKWLoc=*/SourceLocation(), GetID, nullptr); | ||
|
||
if (MemExpr.isInvalid()) | ||
return ExprError(); | ||
|
||
return S.ActOnCallExpr(S.getCurScope(), MemExpr.get(), Loc, {}, Loc); | ||
} | ||
} | ||
|
||
// ((*t1).*f)(t2, ..., tN) when f is a pointer to a member function of a | ||
// class T and t1 does not satisfy the previous two items; | ||
|
||
return S.ActOnUnaryOp(S.getCurScope(), Loc, tok::star, Args[1]); | ||
}(); | ||
|
||
if (ObjectArg.isInvalid()) | ||
return ExprError(); | ||
|
||
ExprResult BinOp = S.ActOnBinOp(S.getCurScope(), TheCall->getBeginLoc(), | ||
tok::periodstar, ObjectArg.get(), Args[0]); | ||
if (BinOp.isInvalid()) | ||
return ExprError(); | ||
|
||
if (MPT->isMemberDataPointer()) | ||
return BinOp; | ||
|
||
auto *MemCall = new (S.Context) | ||
ParenExpr(SourceLocation(), SourceLocation(), BinOp.get()); | ||
|
||
return S.ActOnCallExpr(S.getCurScope(), MemCall, TheCall->getBeginLoc(), | ||
Args.drop_front(2), TheCall->getRParenLoc()); | ||
} | ||
return S.ActOnCallExpr(S.getCurScope(), Args.front(), TheCall->getBeginLoc(), | ||
Args.drop_front(), TheCall->getRParenLoc()); | ||
} | ||
|
||
ExprResult | ||
Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID, | ||
CallExpr *TheCall) { | ||
|
@@ -2420,6 +2513,8 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID, | |
return BuiltinShuffleVector(TheCall); | ||
// TheCall will be freed by the smart pointer here, but that's fine, since | ||
// BuiltinShuffleVector guts it, but then doesn't release it. | ||
case Builtin::BI__builtin_invoke: | ||
return BuiltinInvoke(*this, TheCall); | ||
case Builtin::BI__builtin_prefetch: | ||
if (BuiltinPrefetch(TheCall)) | ||
return ExprError(); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1579,6 +1579,58 @@ ExprResult Sema::ActOnTypeTrait(TypeTrait Kind, SourceLocation KWLoc, | |
return BuildTypeTrait(Kind, KWLoc, ConvertedArgs, RParenLoc); | ||
} | ||
|
||
bool Sema::BuiltinIsBaseOf(SourceLocation RhsTLoc, QualType LhsT, | ||
QualType RhsT) { | ||
// C++0x [meta.rel]p2 | ||
// Base is a base class of Derived without regard to cv-qualifiers or | ||
// Base and Derived are not unions and name the same class type without | ||
// regard to cv-qualifiers. | ||
|
||
const RecordType *lhsRecord = LhsT->getAs<RecordType>(); | ||
const RecordType *rhsRecord = RhsT->getAs<RecordType>(); | ||
if (!rhsRecord || !lhsRecord) { | ||
const ObjCObjectType *LHSObjTy = LhsT->getAs<ObjCObjectType>(); | ||
const ObjCObjectType *RHSObjTy = RhsT->getAs<ObjCObjectType>(); | ||
if (!LHSObjTy || !RHSObjTy) | ||
return false; | ||
|
||
ObjCInterfaceDecl *BaseInterface = LHSObjTy->getInterface(); | ||
ObjCInterfaceDecl *DerivedInterface = RHSObjTy->getInterface(); | ||
if (!BaseInterface || !DerivedInterface) | ||
return false; | ||
|
||
if (RequireCompleteType(RhsTLoc, RhsT, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think this diagnostic is tested. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've added a test for the case below. If anybody can inform me how to create an ObjCObjectType I'm happy to add a test for this as well (though I'm not sure testing this is that useful). |
||
diag::err_incomplete_type_used_in_type_trait_expr)) | ||
return false; | ||
|
||
return BaseInterface->isSuperClassOf(DerivedInterface); | ||
} | ||
|
||
assert(Context.hasSameUnqualifiedType(LhsT, RhsT) == | ||
(lhsRecord == rhsRecord)); | ||
|
||
// Unions are never base classes, and never have base classes. | ||
// It doesn't matter if they are complete or not. See PR#41843 | ||
if (lhsRecord && lhsRecord->getDecl()->isUnion()) | ||
return false; | ||
if (rhsRecord && rhsRecord->getDecl()->isUnion()) | ||
return false; | ||
|
||
if (lhsRecord == rhsRecord) | ||
return true; | ||
|
||
// C++0x [meta.rel]p2: | ||
// If Base and Derived are class types and are different types | ||
// (ignoring possible cv-qualifiers) then Derived shall be a | ||
// complete type. | ||
if (RequireCompleteType(RhsTLoc, RhsT, | ||
philnik777 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
diag::err_incomplete_type_used_in_type_trait_expr)) | ||
return false; | ||
|
||
return cast<CXXRecordDecl>(rhsRecord->getDecl()) | ||
->isDerivedFrom(cast<CXXRecordDecl>(lhsRecord->getDecl())); | ||
} | ||
|
||
static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT, | ||
const TypeSourceInfo *Lhs, | ||
const TypeSourceInfo *Rhs, | ||
|
@@ -1590,58 +1642,9 @@ static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT, | |
"Cannot evaluate traits of dependent types"); | ||
|
||
switch (BTT) { | ||
case BTT_IsBaseOf: { | ||
// C++0x [meta.rel]p2 | ||
// Base is a base class of Derived without regard to cv-qualifiers or | ||
// Base and Derived are not unions and name the same class type without | ||
// regard to cv-qualifiers. | ||
|
||
const RecordType *lhsRecord = LhsT->getAs<RecordType>(); | ||
const RecordType *rhsRecord = RhsT->getAs<RecordType>(); | ||
if (!rhsRecord || !lhsRecord) { | ||
const ObjCObjectType *LHSObjTy = LhsT->getAs<ObjCObjectType>(); | ||
const ObjCObjectType *RHSObjTy = RhsT->getAs<ObjCObjectType>(); | ||
if (!LHSObjTy || !RHSObjTy) | ||
return false; | ||
|
||
ObjCInterfaceDecl *BaseInterface = LHSObjTy->getInterface(); | ||
ObjCInterfaceDecl *DerivedInterface = RHSObjTy->getInterface(); | ||
if (!BaseInterface || !DerivedInterface) | ||
return false; | ||
|
||
if (Self.RequireCompleteType( | ||
Rhs->getTypeLoc().getBeginLoc(), RhsT, | ||
diag::err_incomplete_type_used_in_type_trait_expr)) | ||
return false; | ||
|
||
return BaseInterface->isSuperClassOf(DerivedInterface); | ||
} | ||
|
||
assert(Self.Context.hasSameUnqualifiedType(LhsT, RhsT) == | ||
(lhsRecord == rhsRecord)); | ||
|
||
// Unions are never base classes, and never have base classes. | ||
// It doesn't matter if they are complete or not. See PR#41843 | ||
if (lhsRecord && lhsRecord->getDecl()->isUnion()) | ||
return false; | ||
if (rhsRecord && rhsRecord->getDecl()->isUnion()) | ||
return false; | ||
case BTT_IsBaseOf: | ||
return Self.BuiltinIsBaseOf(Rhs->getTypeLoc().getBeginLoc(), LhsT, RhsT); | ||
|
||
if (lhsRecord == rhsRecord) | ||
return true; | ||
|
||
// C++0x [meta.rel]p2: | ||
// If Base and Derived are class types and are different types | ||
// (ignoring possible cv-qualifiers) then Derived shall be a | ||
// complete type. | ||
if (Self.RequireCompleteType( | ||
Rhs->getTypeLoc().getBeginLoc(), RhsT, | ||
diag::err_incomplete_type_used_in_type_trait_expr)) | ||
return false; | ||
|
||
return cast<CXXRecordDecl>(rhsRecord->getDecl()) | ||
->isDerivedFrom(cast<CXXRecordDecl>(lhsRecord->getDecl())); | ||
} | ||
case BTT_IsVirtualBaseOf: { | ||
const RecordType *BaseRecord = LhsT->getAs<RecordType>(); | ||
const RecordType *DerivedRecord = RhsT->getAs<RecordType>(); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
// RUN: %clang_cc1 -triple=x86_64-linux-gnu -emit-llvm -o - %s | FileCheck %s | ||
|
||
extern "C" void* memcpy(void*, const void*, decltype(sizeof(int))); | ||
void func(); | ||
|
||
namespace std { | ||
template <class T> | ||
class reference_wrapper { | ||
T* ptr; | ||
|
||
public: | ||
T& get() { return *ptr; } | ||
}; | ||
} // namespace std | ||
|
||
struct Callable { | ||
void operator()() {} | ||
|
||
void func(); | ||
}; | ||
|
||
extern "C" void call1() { | ||
__builtin_invoke(func); | ||
__builtin_invoke(Callable{}); | ||
__builtin_invoke(memcpy, nullptr, nullptr, 0); | ||
|
||
// CHECK: define dso_local void @call1 | ||
// CHECK-NEXT: entry: | ||
// CHECK-NEXT: %ref.tmp = alloca %struct.Callable, align 1 | ||
// CHECK-NEXT: call void @_Z4funcv() | ||
// CHECK-NEXT: call void @_ZN8CallableclEv(ptr noundef nonnull align 1 dereferenceable(1) %ref.tmp) | ||
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 1 null, ptr align 1 null, i64 0, i1 false) | ||
// CHECK-NEXT: ret void | ||
} | ||
|
||
extern "C" void call_memptr(std::reference_wrapper<Callable> wrapper) { | ||
__builtin_invoke(&Callable::func, wrapper); | ||
|
||
// CHECK: define dso_local void @call_memptr | ||
// CHECK-NEXT: entry: | ||
// CHECK-NEXT: %wrapper = alloca %"class.std::reference_wrapper", align 8 | ||
// CHECK-NEXT: %coerce.dive = getelementptr inbounds nuw %"class.std::reference_wrapper", ptr %wrapper, i32 0, i32 0 | ||
// CHECK-NEXT: store ptr %wrapper.coerce, ptr %coerce.dive, align 8 | ||
// CHECK-NEXT: %call = call noundef nonnull align 1 dereferenceable(1) ptr @_ZNSt17reference_wrapperI8CallableE3getEv(ptr noundef nonnull align 8 dereferenceable(8) %wrapper) | ||
// CHECK-NEXT: %0 = getelementptr inbounds i8, ptr %call, i64 0 | ||
// CHECK-NEXT: br i1 false, label %memptr.virtual, label %memptr.nonvirtual | ||
// CHECK-EMPTY: | ||
// CHECK-NEXT: memptr.virtual: | ||
// CHECK-NEXT: %vtable = load ptr, ptr %0, align 8 | ||
// CHECK-NEXT: %1 = getelementptr i8, ptr %vtable, i64 sub (i64 ptrtoint (ptr @_ZN8Callable4funcEv to i64), i64 1), !nosanitize !2 | ||
// CHECK-NEXT: %memptr.virtualfn = load ptr, ptr %1, align 8, !nosanitize !2 | ||
// CHECK-NEXT: br label %memptr.end | ||
// CHECK-EMPTY: | ||
// CHECK-NEXT: memptr.nonvirtual: | ||
// CHECK-NEXT: br label %memptr.end | ||
// CHECK-EMPTY: | ||
// CHECK-NEXT: memptr.end: | ||
// CHECK-NEXT: %2 = phi ptr [ %memptr.virtualfn, %memptr.virtual ], [ @_ZN8Callable4funcEv, %memptr.nonvirtual ] | ||
// CHECK-NEXT: call void %2(ptr noundef nonnull align 1 dereferenceable(1) %0) | ||
// CHECK-NEXT: ret void | ||
} |
Uh oh!
There was an error while loading. Please reload this page.