Index: include/clang/AST/ASTContext.h =================================================================== --- include/clang/AST/ASTContext.h +++ include/clang/AST/ASTContext.h @@ -2002,6 +2002,9 @@ /// No error GE_None, + /// Missing a type + GE_Missing_type, + /// Missing a type from GE_Missing_stdio, Index: include/clang/Basic/Attr.td =================================================================== --- include/clang/Basic/Attr.td +++ include/clang/Basic/Attr.td @@ -183,6 +183,7 @@ class TypeArgument : Argument; class UnsignedArgument : Argument; class VariadicUnsignedArgument : Argument; +class VariadicSignedArgument : Argument; class VariadicExprArgument : Argument; class VariadicStringArgument : Argument; class VariadicIdentifierArgument : Argument; @@ -1197,6 +1198,14 @@ let Documentation = [Undocumented]; } +def Callback : InheritableAttr { + let Spellings = [Clang<"callback">]; + let Args = [UnsignedArgument<"CalleeIdx">, + VariadicSignedArgument<"PayloadIndices">]; + let Subjects = SubjectList<[Function]>; + let Documentation = [CallbackDocs]; +} + def GNUInline : InheritableAttr { let Spellings = [GCC<"gnu_inline">]; let Subjects = SubjectList<[Function]>; Index: include/clang/Basic/AttrDocs.td =================================================================== --- include/clang/Basic/AttrDocs.td +++ include/clang/Basic/AttrDocs.td @@ -3704,6 +3704,46 @@ }]; } +def CallbackDocs : Documentation { + let Category = DocCatVariable; + let Content = [{ +The ``callback`` attribute specifies that the annotated function may invoke the +specified callback zero or more times. The callback, as well as the passed +arguments, are identified by their parameter position (starting with +1!) in the annotated function. The first position identifies the callback +callee and the following indices are the forwarded arguments. The callback +callee is required to be callable with the number, and order, of the specified +arguments. The index '0' is used to represent an implicit "this" pointer in +class methods. If there is no implicit "this" pointer, the index '0' shall not +be used. The index '-1' represents an unknown callback callee argument. This +can be a value which is not present in the declared parameter list, or one +that is passed to the callback callee but potentially inspected, captured, or +modified. + +The ``callback`` attribute, which are directly translated to ``callback`` +metadata , make the +connection between the call to the annotated function and the callback callee. +This can enable interprocedural optimizations which were otherwise impossible. +If a function parameter is mentioned in the ``callback`` attribute, through its +position, it is undefined if that parameter is used for anything other than the +actual callback. Inspected, captured, or modified parameters shall not be +listed in the ``callback`` metadata. + +An example for the callback performed by `pthread_create` is shown below. The +explicit attribute annotation indicates that the third parameter +(`start_routine`) is called zero or more times by the `pthread_create` function, +and that the fourth parameter (`arg`) is passed along. Note that the callback +behavior of `pthread_create` and `qsort_r` is automatically recognized by clang. + + .. code-block:: c + + __attribute__((callback (3, 4))) + int pthread_create(pthread_t *thread, const pthread_attr_t *attr, + void *(*start_routine) (void *), void *arg); + + }]; +} + def GnuInlineDocs : Documentation { let Category = DocCatFunction; let Content = [{ Index: include/clang/Basic/Builtins.h =================================================================== --- include/clang/Basic/Builtins.h +++ include/clang/Basic/Builtins.h @@ -194,6 +194,13 @@ /// argument and whether this function as a va_list argument. bool isScanfLike(unsigned ID, unsigned &FormatIdx, bool &HasVAListArg); + /// Determine whether this builtin has callback behavior (see + /// llvm::AbstractCallSites for details). If so, set the index to the + /// callback callee argument and the callback payload arguments. Note that + /// we use LLVM indices starting at 0 here! + bool performsCallback(unsigned ID, unsigned &CalleeIdx, + llvm::SmallVectorImpl &PayloadIndices) const; + /// Return true if this function has no side effects and doesn't /// read memory, except for possibly errno. /// Index: include/clang/Basic/Builtins.def =================================================================== --- include/clang/Basic/Builtins.def +++ include/clang/Basic/Builtins.def @@ -93,6 +93,7 @@ // j -> returns_twice (like setjmp) // u -> arguments are not evaluated for their side-effects // V:N: -> requires vectors of at least N bits to be legal +// C -> callback behavior: argument N is called with argument M_0, ..., M_k as payload // FIXME: gcc has nonnull #if defined(BUILTIN) && !defined(LIBBUILTIN) @@ -938,6 +939,7 @@ // all languages, because losing this attribute would result in miscompilation // when these functions are used in non-GNU mode. PR16138. LIBBUILTIN(alloca, "v*z", "f", "stdlib.h", ALL_GNU_LANGUAGES) +LIBBUILTIN(qsort_r, "", "fC<3,-1,-1,4>", "stdlib.h", ALL_GNU_LANGUAGES) // POSIX string.h LIBBUILTIN(stpcpy, "c*c*cC*", "f", "string.h", ALL_GNU_LANGUAGES) LIBBUILTIN(stpncpy, "c*c*cC*z", "f", "string.h", ALL_GNU_LANGUAGES) @@ -956,6 +958,9 @@ // POSIX unistd.h LIBBUILTIN(_exit, "vi", "fr", "unistd.h", ALL_GNU_LANGUAGES) LIBBUILTIN(vfork, "p", "fj", "unistd.h", ALL_LANGUAGES) +// POSIX pthread.h +LIBBUILTIN(pthread_create, "", "fC<2,3>", "pthread.h", ALL_GNU_LANGUAGES) + // POSIX setjmp.h LIBBUILTIN(_setjmp, "iJ", "fj", "setjmp.h", ALL_LANGUAGES) Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -2588,6 +2588,24 @@ def err_format_attribute_implicit_this_format_string : Error< "format attribute cannot specify the implicit this argument as the format " "string">; +def err_callback_attribute_no_callee : Error< + "no callback callee argument index specified">; +def err_callback_attribute_multiple : Error< + "multiple 'callback' attributes specified">; +def err_callback_attribute_argument_index_oob : Error< + "'callback' argument at position %0 is out-of-bounds">; +def err_callback_incomplete_function_type : Error< + "'callback' function has incomplete function type">; +def err_callback_callee_no_function_type : Error< + "'callback' callee does not have function type">; +def err_callback_callee_is_variadic : Error< + "'callback' callee shall not be variadic">; +def err_callback_implicit_this_not_available : Error< + "'callback' argument at position %0 specifies unawailable implicit 'this'">; +def err_callback_too_few_arguments : Error< + "'callback' attribute specifies too few arguments">; +def err_callback_too_many_arguments : Error< + "'callback' attribute specifies too many arguments">; def err_init_method_bad_return_type : Error< "init methods must return an object pointer type, not %0">; def err_attribute_invalid_size : Error< Index: lib/AST/ASTContext.cpp =================================================================== --- lib/AST/ASTContext.cpp +++ lib/AST/ASTContext.cpp @@ -9517,6 +9517,10 @@ GetBuiltinTypeError &Error, unsigned *IntegerConstantArgs) const { const char *TypeStr = BuiltinInfo.getTypeString(Id); + if (TypeStr[0] == '\0') { + Error = GE_Missing_type; + return {}; + } SmallVector ArgTypes; Index: lib/Basic/Builtins.cpp =================================================================== --- lib/Basic/Builtins.cpp +++ lib/Basic/Builtins.cpp @@ -156,6 +156,33 @@ return isLike(ID, FormatIdx, HasVAListArg, "sS"); } +bool Builtin::Context::performsCallback( + unsigned ID, unsigned &CalleeIdx, + SmallVectorImpl &PayloadIndices) const { + const char *CalleePos = ::strchr(getRecord(ID).Attributes, 'C'); + if (!CalleePos) + return false; + + ++CalleePos; + assert(*CalleePos == '<' && + "Callback callee specifier must be followed by a '<'"); + ++CalleePos; + + char *EndPos; + CalleeIdx = ::strtol(CalleePos, &EndPos, 10); + + while (*EndPos == ',') { + const char *PayloadPos = EndPos + 1; + + int PayloadIdx = ::strtol(PayloadPos, &EndPos, 10); + PayloadIndices.push_back(PayloadIdx); + } + + assert(*EndPos == '>' && "Callback callee specifier must end with a '>'"); + + return true; +} + bool Builtin::Context::canBeRedeclared(unsigned ID) const { return ID == Builtin::NotBuiltin || ID == Builtin::BI__va_start || Index: lib/CodeGen/CGOpenMPRuntime.cpp =================================================================== --- lib/CodeGen/CGOpenMPRuntime.cpp +++ lib/CodeGen/CGOpenMPRuntime.cpp @@ -1674,6 +1674,18 @@ auto *FnTy = llvm::FunctionType::get(CGM.VoidTy, TypeParams, /*isVarArg*/ true); RTLFn = CGM.CreateRuntimeFunction(FnTy, "__kmpc_fork_call"); + if (auto *F = dyn_cast(RTLFn)) { + if (!F->hasMetadata(llvm::LLVMContext::MD_callback)) { + llvm::MDBuilder MDB(F->getContext()); + // Annotate the callback behavior of the __kmpc_fork_call: + // - The callback callee is argument number 2 (microtask). + // - The first two arguments of the callback callee are unknown (-1). + // - All variadic arguments to the __kmpc_fork_call are passed to the + // callback callee. + F->addMetadata(llvm::LLVMContext::MD_callback, + *MDB.createCallback(2, {-1, -1}, /* VarArg */ true)); + } + } break; } case OMPRTL__kmpc_global_thread_num: { @@ -2081,6 +2093,18 @@ auto *FnTy = llvm::FunctionType::get(CGM.VoidTy, TypeParams, /*isVarArg*/ true); RTLFn = CGM.CreateRuntimeFunction(FnTy, "__kmpc_fork_teams"); + if (auto *F = dyn_cast(RTLFn)) { + if (!F->hasMetadata(llvm::LLVMContext::MD_callback)) { + llvm::MDBuilder MDB(F->getContext()); + // Annotate the callback behavior of the __kmpc_fork_teams: + // - The callback callee is argument number 2 (microtask). + // - The first two arguments of the callback callee are unknown (-1). + // - All variadic arguments to the __kmpc_fork_teams are passed to the + // callback callee. + F->addMetadata(llvm::LLVMContext::MD_callback, + *MDB.createCallback(2, {-1, -1}, /* VarArg */ true)); + } + } break; } case OMPRTL__kmpc_taskloop: { Index: lib/CodeGen/CodeGenModule.cpp =================================================================== --- lib/CodeGen/CodeGenModule.cpp +++ lib/CodeGen/CodeGenModule.cpp @@ -1600,6 +1600,18 @@ if (getLangOpts().OpenMP && FD->hasAttr()) getOpenMPRuntime().emitDeclareSimdFunction(FD, F); + + if (const auto *CB = FD->getAttr()) { + // Annotate the callback behavior as metadata: + // - The callback callee (as argument number). + // - The callback payloads (as argument numbers). + llvm::MDBuilder MDB(F->getContext()); + ArrayRef PayloadIndices(CB->payloadIndices_begin(), + CB->payloadIndices_size()); + F->addMetadata(llvm::LLVMContext::MD_callback, + *MDB.createCallback(CB->getCalleeIdx(), PayloadIndices, + /* VarArg */ false)); + } } void CodeGenModule::addUsedGlobal(llvm::GlobalValue *GV) { Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -1927,10 +1927,13 @@ Context.setObjCSuperType(Context.getTagDeclType(TD)); } -static StringRef getHeaderName(ASTContext::GetBuiltinTypeError Error) { +static StringRef getHeaderName(Builtin::Context &BuiltinInfo, unsigned ID, + ASTContext::GetBuiltinTypeError Error) { switch (Error) { case ASTContext::GE_None: return ""; + case ASTContext::GE_Missing_type: + return BuiltinInfo.getHeaderName(ID); case ASTContext::GE_Missing_stdio: return "stdio.h"; case ASTContext::GE_Missing_setjmp: @@ -1955,7 +1958,8 @@ if (Error) { if (ForRedeclaration) Diag(Loc, diag::warn_implicit_decl_requires_sysheader) - << getHeaderName(Error) << Context.BuiltinInfo.getName(ID); + << getHeaderName(Context.BuiltinInfo, ID, Error) + << Context.BuiltinInfo.getName(ID); return nullptr; } @@ -13578,6 +13582,18 @@ FD->getLocation())); } + // Automatically recognized callbacks are currently limited to a single + // payload argument. + unsigned CallbackCalleeIdx; + SmallVector CallbackPayloadIndices; + if (Context.BuiltinInfo.performsCallback(BuiltinID, CallbackCalleeIdx, + CallbackPayloadIndices)) { + if (!FD->hasAttr()) + FD->addAttr(CallbackAttr::CreateImplicit( + Context, CallbackCalleeIdx, CallbackPayloadIndices.data(), + CallbackPayloadIndices.size(), FD->getLocation())); + } + // Mark const if we don't care about errno and that is the only thing // preventing the function from being const. This allows IRgen to use LLVM // intrinsics for such functions. Index: lib/Sema/SemaDeclAttr.cpp =================================================================== --- lib/Sema/SemaDeclAttr.cpp +++ lib/Sema/SemaDeclAttr.cpp @@ -3455,6 +3455,103 @@ D->addAttr(NewAttr); } +/// Handle __attribute__((callback(CalleeIdx, PayloadIdx0, ...))) attributes. +static void handleCallbackAttr(Sema &S, Decl *D, const ParsedAttr &AL) { + // The index that identifies the callback callee is mandatory. + assert(AL.getNumArgs() > 0); + + bool HasImplicitThisParam = isInstanceMethod(D); + int32_t NumArgs = getFunctionOrMethodNumParams(D); + + // Helper to extract a number and validate its range. + auto ExtractArgIdx = [&](int32_t &Idx, unsigned Pos, int32_t Min) -> bool { + Expr *IdxExpr = AL.getArgAsExpr(Pos); + if (!checkUInt32Argument(S, AL, IdxExpr, (uint32_t &)Idx, Pos + 1, false)) + return false; + + if (Idx < Min || Idx > NumArgs) { + S.Diag(AL.getLoc(), diag::err_callback_attribute_argument_index_oob) + << (Pos + 1) << IdxExpr->getSourceRange(); + return false; + } + + return true; + }; + + // If the callee index is 0 it is invalid (we start counting with 1). + int CalleeIdx; + if (!ExtractArgIdx(CalleeIdx, 0, 1)) + return; + assert(CalleeIdx > 0 && CalleeIdx <= NumArgs); + + SmallVector PayloadIndices; + for (unsigned i = 1, e = AL.getNumArgs(); i < e; i++) { + // A payload index can be -1 to indicate an unknown value, 0 to indicate the + // implicit "this" argument in C++, or a positive value up to NumArgs. + int PayloadIdx; + if (!ExtractArgIdx(PayloadIdx, i, -1)) + return; + if (PayloadIdx == 0 && !HasImplicitThisParam) { + S.Diag(AL.getLoc(), diag::err_callback_implicit_this_not_available) + << (i + 1) << AL.getArgAsExpr(i)->getSourceRange(); + return; + } + + // Adjust for the case we do not have an implicit "this" parameter. In this + // case we decrease all positive values by 1 to get LLVM argument indices. + PayloadIndices.push_back(std::max(-1, PayloadIdx - !HasImplicitThisParam)); + } + + const Type *CalleeType = + getFunctionOrMethodParamType(D, CalleeIdx - 1).getTypePtr(); + if (!CalleeType || !CalleeType->isFunctionPointerType()) { + S.Diag(AL.getLoc(), diag::err_callback_callee_no_function_type) + << AL.getRange(); + return; + } + + const Type *CalleeFnType = + CalleeType->getPointeeType()->getUnqualifiedDesugaredType(); + + // TODO: Check the type of the callee arguments. + + const auto *CalleeFnProtoType = dyn_cast(CalleeFnType); + if (!CalleeFnProtoType) { + S.Diag(AL.getLoc(), diag::err_callback_callee_no_function_type) + << AL.getRange(); + return; + } + + if (CalleeFnProtoType->getNumParams() > PayloadIndices.size()) { + S.Diag(AL.getLoc(), diag::err_callback_too_few_arguments) << AL.getRange(); + return; + } + + if (CalleeFnProtoType->getNumParams() < PayloadIndices.size()) { + S.Diag(AL.getLoc(), diag::err_callback_too_many_arguments) << AL.getRange(); + return; + } + + if (CalleeFnProtoType->isVariadic()) { + S.Diag(AL.getLoc(), diag::err_callback_callee_is_variadic) << AL.getRange(); + return; + } + + // Do not allow multiple callback attributes. + if (D->hasAttr()) { + S.Diag(AL.getLoc(), diag::err_callback_attribute_multiple) << AL.getRange(); + return; + } + + // Adjust for the case we do not have an implicit "this" parameter. In this + // case we decrease all positive values by 1 to get LLVM argument indices. + CalleeIdx = CalleeIdx - !HasImplicitThisParam; + + D->addAttr(::new (S.Context) CallbackAttr( + AL.getRange(), S.Context, CalleeIdx, PayloadIndices.data(), + PayloadIndices.size(), AL.getAttributeSpellingListIndex())); +} + static void handleTransparentUnionAttr(Sema &S, Decl *D, const ParsedAttr &AL) { // Try to find the underlying union declaration. RecordDecl *RD = nullptr; @@ -6272,6 +6369,9 @@ case ParsedAttr::AT_FormatArg: handleFormatArgAttr(S, D, AL); break; + case ParsedAttr::AT_Callback: + handleCallbackAttr(S, D, AL); + break; case ParsedAttr::AT_CUDAGlobal: handleGlobalAttr(S, D, AL); break; Index: test/Analysis/retain-release.m =================================================================== --- test/Analysis/retain-release.m +++ test/Analysis/retain-release.m @@ -1160,10 +1160,10 @@ //===----------------------------------------------------------------------===// // clang false positive: retained instance passed to -// thread in pthread_create marked as leak +// thread in my_pthread_create marked as leak // // Until we have full IPA, the analyzer should stop tracking the reference -// count of objects passed to pthread_create. +// count of objects passed to my_pthread_create. // //===----------------------------------------------------------------------===// @@ -1176,8 +1176,8 @@ typedef unsigned long __darwin_pthread_key_t; typedef __darwin_pthread_key_t pthread_key_t; -int pthread_create(pthread_t *, const pthread_attr_t *, - void *(*)(void *), void *); +int my_pthread_create(pthread_t *, const pthread_attr_t *, + void *(*)(void *), void *); int pthread_setspecific(pthread_key_t key, const void *value); @@ -1187,7 +1187,7 @@ } void rdar_7299394(pthread_attr_t *attr, pthread_t *thread, void *args) { NSNumber *number = [[NSNumber alloc] initWithInt:5]; // no-warning - pthread_create(thread, attr, rdar_7299394_start_routine, number); + my_pthread_create(thread, attr, rdar_7299394_start_routine, number); } void rdar_7299394_positive(pthread_attr_t *attr, pthread_t *thread) { NSNumber *number = [[NSNumber alloc] initWithInt:5]; // expected-warning{{leak}} Index: test/CodeGen/attr-callback.c =================================================================== --- /dev/null +++ test/CodeGen/attr-callback.c @@ -0,0 +1,20 @@ +// RUN: %clang_cc1 -triple i386-unknown-unknown %s -emit-llvm -o - | FileCheck %s + +void cb0(void); + +// CHECK-DAG: !callback ![[cid0:[0-9]+]] void @no_args +__attribute__((callback(1))) void no_args(void (*callback)(void)); + +// CHECK-DAG: @args_1({{[^#]*#[0-9]+}} !callback ![[cid1:[0-9]+]] +__attribute__((callback(1, 2, 3))) void args_1(void (*callback)(int, double), int a, double b) { no_args(cb0); } + +// CHECK-DAG: !callback ![[cid2:[0-9]+]] void @args_2 +__attribute__((callback(2, 3, 3))) void args_2(int a, void (*callback)(double, double), double b); + +// CHECK-DAG: @args_3({{[^#]*#[0-9]+}} !callback ![[cid3:[0-9]+]] +__attribute__((callback(2, -1, -1))) void args_3(int a, void (*callback)(double, double), double b) { args_2(a, callback, b); } + +// CHECK-DAG: ![[cid0]] = !{i1 false, i64 0} +// CHECK-DAG: ![[cid1]] = !{i1 false, i64 0, i64 1, i64 2} +// CHECK-DAG: ![[cid2]] = !{i1 false, i64 1, i64 2, i64 2} +// CHECK-DAG: ![[cid3]] = !{i1 false, i64 1, i64 -1, i64 -1} Index: test/CodeGen/callback_annotated.c =================================================================== --- /dev/null +++ test/CodeGen/callback_annotated.c @@ -0,0 +1,70 @@ +// RUN: %clang_cc1 -triple i386-unknown-unknown -fopenmp -O1 %s -emit-llvm -o - | FileCheck %s --check-prefix=RUN1 +// RUN: %clang_cc1 -triple i386-unknown-unknown -fopenmp -O1 %s -emit-llvm -o - | FileCheck %s --check-prefix=RUN2 +// RUN: %clang_cc1 -triple i386-unknown-unknown -fopenmp -O1 %s -emit-llvm -o - | opt -ipconstprop -S | FileCheck --check-prefix=IPCP %s + +// RUN1-DAG: @broker0({{[^#]*#[0-9]+}} !callback ![[cid0:[0-9]+]] +__attribute__((callback (1, 2))) +void* broker0(void* (*callee)(void *), void *payload) { + return callee(payload); +} + +// RUN1-DAG: @broker1({{[^#]*#[0-9]+}} !callback ![[cid1:[0-9]+]] +__attribute__((callback (2, 1))) +void* broker1(void *payload, void* (*callee)(void *)) { + return broker0(callee, payload); +} + +// RUN1-DAG: declare !callback ![[cid2:[0-9]+]] i8* @broker2 +__attribute__((callback (1))) +void* broker2(void (*callee)(void)); + +// RUN1-DAG: declare !callback ![[cid3:[0-9]+]] i8* @broker3 +__attribute__((callback (4, 1, 2, 3))) +void* broker3(int, int, int, int (*callee)(int, int, int), int); + +// RUN1-DAG: declare !callback ![[cid4:[0-9]+]] i8* @broker4 +__attribute__((callback (4, -1, 1, -1))) +void* broker4(int, int, int, int (*callee)(int, int, int), int); + +// RUN1-DAG: declare !callback ![[cid5:[0-9]+]] i8* @broker5 +__attribute__((callback (4, 5, 5, 2))) +void* broker5(int, int, int, int (*callee)(int, int, int), int); + + +static void *VoidPtr2VoidPtr(void *payload) { +// RUN2: ret i8* %payload +// IPCP: ret i8* null + return payload; +} + +static int ThreeInt2Int(int a, int b, int c) { +// RUN2: define internal i32 @ThreeInt2Int(i32 %a, i32 %b, i32 %c) +// RUN2-NEXT: entry: +// RUN2-NEXT: %mul = mul nsw i32 %b, %a +// RUN2-NEXT: %add = add nsw i32 %mul, %c +// RUN2-NEXT: ret i32 %add + +// IPCP: define internal i32 @ThreeInt2Int(i32 %a, i32 %b, i32 %c) +// IPCP-NEXT: entry: +// IPCP-NEXT: %mul = mul nsw i32 4, %a +// IPCP-NEXT: %add = add nsw i32 %mul, %c +// IPCP-NEXT: ret i32 %add + + return a * b + c; +} + +void foo() { + broker0(VoidPtr2VoidPtr, 0l); + broker1(0l, VoidPtr2VoidPtr); + broker2(foo); + broker3(1, 4, 5, ThreeInt2Int, 1); + broker4(4, 2, 7, ThreeInt2Int, 0); + broker5(8, 0, 3, ThreeInt2Int, 4); +} + +// RUN1-DAG: ![[cid0]] = !{i1 false, i64 0, i64 1} +// RUN1-DAG: ![[cid1]] = !{i1 false, i64 1, i64 0} +// RUN1-DAG: ![[cid2]] = !{i1 false, i64 0} +// RUN1-DAG: ![[cid3]] = !{i1 false, i64 3, i64 0, i64 1, i64 2} +// RUN1-DAG: ![[cid4]] = !{i1 false, i64 3, i64 -1, i64 0, i64 -1} +// RUN1-DAG: ![[cid5]] = !{i1 false, i64 3, i64 4, i64 4, i64 1} Index: test/CodeGen/callback_openmp.c =================================================================== --- /dev/null +++ test/CodeGen/callback_openmp.c @@ -0,0 +1,27 @@ +// RUN: %clang_cc1 -triple i386-unknown-unknown -fopenmp -O1 %s -emit-llvm -o - | FileCheck %s +// RUN: %clang_cc1 -triple i386-unknown-unknown -fopenmp -O1 %s -emit-llvm -o - | opt -ipconstprop -S | FileCheck --check-prefix=IPCP %s + +// CHECK: declare !callback ![[cid:[0-9]+]] void @__kmpc_fork_call +// CHECK: declare !callback ![[cid]] void @__kmpc_fork_teams +// CHECK: ![[cid]] = !{i1 true, i64 2, i64 -1, i64 -1} + +void work1(int, int); +void work2(int, int); +void work12(int, int); + +void foo(int q) { + int p = 2; + + #pragma omp parallel firstprivate(q, p) + work1(p, q); +// IPCP: call void @work1(i32 2, i32 %{{[._a-zA-Z0-9]*}}) + + #pragma omp parallel for firstprivate(p, q) + for (int i = 0; i < q; i++) + work2(i, p); +// IPCP: call void @work2(i32 %{{[._a-zA-Z0-9]*}}, i32 2) + + #pragma omp target teams firstprivate(p) + work12(p, p); +// IPCP: call void @work12(i32 2, i32 2) +} Index: test/CodeGen/callback_pthread_create.c =================================================================== --- /dev/null +++ test/CodeGen/callback_pthread_create.c @@ -0,0 +1,31 @@ +// RUN: %clang -O1 %s -S -c -emit-llvm -o - | FileCheck %s +// RUN: %clang -O1 %s -S -c -emit-llvm -o - | opt -ipconstprop -S | FileCheck --check-prefix=IPCP %s + +// CHECK: declare !callback ![[cid:[0-9]+]] dso_local i32 @pthread_create +// CHECK: ![[cid]] = !{i1 false, i64 2, i64 3} + +#include + +const int GlobalVar = 0; + +static void *callee0(void *payload) { +// IPCP: define internal i8* @callee0 +// IPCP-NEXT: entry: +// IPCP-NEXT: ret i8* null + return payload; +} + +static void *callee1(void *payload) { +// IPCP: define internal i8* @callee1 +// IPCP-NEXT: entry: +// IPCP-NEXT: ret i8* bitcast (i32* @GlobalVar to i8*) + return payload; +} + +void foo() { + pthread_t MyFirstThread; + pthread_create(&MyFirstThread, NULL, callee0, NULL); + + pthread_t MySecondThread; + pthread_create(&MySecondThread, NULL, callee1, (void *)&GlobalVar); +} Index: test/CodeGen/callback_qsort_r.c =================================================================== --- /dev/null +++ test/CodeGen/callback_qsort_r.c @@ -0,0 +1,34 @@ +// RUN: %clang %s -fsyntax-only 2>&1 | FileCheck %s +// RUN: %clang -D_GNU_SOURCE %s -S -c -emit-llvm -o - -O1 | FileCheck %s --check-prefix=IR +// RUN: %clang -D_GNU_SOURCE %s -S -c -emit-llvm -o - -O1 | opt -ipconstprop -instcombine -S | FileCheck --check-prefix=IPCP %s + +// CHECK: declaration of built-in function 'qsort_r' requires inclusion of the header + +#include + +static int callee(const void *v0, const void *v1, void *payload); + +void foo(char *A, int N) { +// IR: declare !callback ![[cid:[0-9]+]] dso_local void @qsort_r + qsort_r(A, N, 1, callee, NULL); +} + +static int callee(const void *v0, const void *v1, void *payload) { +// IR: define internal i32 @callee +// IR-NEXT: entry: +// IR-NEXT: %cmp = icmp eq i8* %payload, null +// IR-NEXT: %cmp1 = icmp ult i8* %v0, %v1 +// IR-NEXT: %cmp2 = icmp ult i8* %v1, %v0 +// IR-NEXT: %cond.in = select i1 %cmp, i1 %cmp1, i1 %cmp2 +// IR-NEXT: %cond = zext i1 %cond.in to i32 +// IR-NEXT: ret i32 %cond + +// IPCP: define internal i32 @callee +// IPCP-NEXT: entry: +// IPCP-NEXT: %cmp1 = icmp ult i8* %v0, %v1 +// IPCP-NEXT: %cond = zext i1 %cmp1 to i32 +// IPCP-NEXT: ret i32 %cond + return payload == 0 ? v0 < v1 : v1 < v0; +} + +// IR: ![[cid]] = !{i1 false, i64 3, i64 -1, i64 -1, i64 4} Index: test/CodeGenCXX/attr-callback.cpp =================================================================== --- /dev/null +++ test/CodeGenCXX/attr-callback.cpp @@ -0,0 +1,48 @@ +// RUN: %clang_cc1 -triple i386-unknown-unknown %s -emit-llvm -o - | FileCheck %s + +struct Base { + + void no_args_1(void (*callback)(void)); + __attribute__((callback(1))) void no_args_2(void (*callback1)(void), void (*callback2)(void)); + + // TODO: There should probably be a warning or even an error for different + // callbacks on the same method. + __attribute__((callback(1))) virtual void + virtual_1(void (*callback)(void)); + + __attribute__((callback(1, 0, -1, 0))) virtual void + this_unknown_this(void (*callback)(Base *, Base *, Base *)); +}; + +// CHECK-DAG: define void @_ZN4Base9no_args_1EPFvvE({{[^!]*!callback}} ![[cid0:[0-9]+]] +__attribute__((callback(1))) void +Base::no_args_1(void (*callback)(void)) { +} + +// CHECK-DAG: define void @_ZN4Base9no_args_2EPFvvES1_({{[^!]*!callback}} ![[cid1:[0-9]+]] +__attribute__((callback(2))) void Base::no_args_2(void (*callback1)(void), void (*callback2)(void)) { +} + +// CHECK-DAG: define void @_ZN4Base17this_unknown_thisEPFvPS_S0_S0_E({{[^!]*!callback}} ![[cid2:[0-9]+]] +void Base::this_unknown_this(void (*callback)(Base *, Base *, Base *)) { +} + +struct Derived_1 : public Base { + __attribute__((callback(1))) virtual void + virtual_1(void (*callback)(void)) override; +}; + +// CHECK-DAG: define void @_ZN9Derived_19virtual_1EPFvvE({{[^!]*!callback}} ![[cid0]] +void Derived_1::virtual_1(void (*callback)(void)) {} + +struct Derived_2 : public Base { + void virtual_1(void (*callback)(void)) override; +}; + +// CHECK-DAG: define void @_ZN9Derived_29virtual_1EPFvvE +// CHECK-NOT: !callback +void Derived_2::virtual_1(void (*callback)(void)) {} + +// CHECK-DAG: ![[cid0]] = !{i1 false, i64 1} +// CHECK-DAG: ![[cid1]] = !{i1 false, i64 2} +// CHECK-DAG: ![[cid2]] = !{i1 false, i64 1, i64 0, i64 -1, i64 0} Index: test/Misc/pragma-attribute-supported-attributes-list.test =================================================================== --- test/Misc/pragma-attribute-supported-attributes-list.test +++ test/Misc/pragma-attribute-supported-attributes-list.test @@ -32,6 +32,7 @@ // CHECK-NEXT: CUDAShared (SubjectMatchRule_variable) // CHECK-NEXT: CXX11NoReturn (SubjectMatchRule_function) // CHECK-NEXT: CallableWhen (SubjectMatchRule_function_is_member) +// CHECK-NEXT: Callback (SubjectMatchRule_function) // CHECK-NEXT: Capability (SubjectMatchRule_record, SubjectMatchRule_type_alias) // CHECK-NEXT: CarriesDependency (SubjectMatchRule_variable_is_parameter, SubjectMatchRule_objc_method, SubjectMatchRule_function) // CHECK-NEXT: Cold (SubjectMatchRule_function) Index: test/OpenMP/parallel_codegen.cpp =================================================================== --- test/OpenMP/parallel_codegen.cpp +++ test/OpenMP/parallel_codegen.cpp @@ -82,9 +82,9 @@ // CHECK-DEBUG-NEXT: } // CHECK-DAG: define linkonce_odr {{.*}}void [[FOO]]({{i32[ ]?[a-z]*}} %argc) -// CHECK-DAG: declare {{.*}}void @__kmpc_fork_call(%struct.ident_t*, i32, void (i32*, i32*, ...)*, ...) +// CHECK-DAG: declare !callback ![[cbid:[0-9]+]] {{.*}}void @__kmpc_fork_call(%struct.ident_t*, i32, void (i32*, i32*, ...)*, ...) // CHECK-DEBUG-DAG: define linkonce_odr void [[FOO]](i32 %argc) -// CHECK-DEBUG-DAG: declare void @__kmpc_fork_call(%struct.ident_t*, i32, void (i32*, i32*, ...)*, ...) +// CHECK-DEBUG-DAG: declare !callback ![[cbid:[0-9]+]] void @__kmpc_fork_call(%struct.ident_t*, i32, void (i32*, i32*, ...)*, ...) // CHECK-DEBUG-DAG: define internal void [[OMP_OUTLINED]](i32* noalias %.global_tid., i32* noalias %.bound_tid., i64 [[VLA_SIZE:%.+]], i32* {{.+}} [[VLA_ADDR:%[^)]+]]) // CHECK-DEBUG-DAG: call void [[OMP_OUTLINED_DEBUG]] @@ -131,5 +131,5 @@ // CHECK: attributes #[[FN_ATTRS]] = {{.+}} nounwind // CHECK-DEBUG: attributes #[[FN_ATTRS]] = {{.+}} nounwind - +// CHECK: ![[cbid]] = !{i1 true, i64 2, i64 -1, i64 -1} #endif Index: test/Sema/attr-callback-broken.c =================================================================== --- /dev/null +++ test/Sema/attr-callback-broken.c @@ -0,0 +1,38 @@ +// RUN: %clang_cc1 %s -verify -fsyntax-only + +__attribute__((callback())) void no_callee(void (*callback)(void)); // expected-error {{'callback' attribute takes at least 1 argument}} + +__attribute__((callback(1, 1))) void too_many_args_1(void (*callback)(void)) {} // expected-error {{'callback' attribute specifies too many arguments}} +__attribute__((callback(1, -1))) void too_many_args_2(double (*callback)(void)); // expected-error {{'callback' attribute specifies too many arguments}} +__attribute__((callback(1, 2, 2))) void too_many_args_3(void (*callback)(int), int); // expected-error {{'callback' attribute specifies too many arguments}} + +__attribute__((callback(1, 2))) void too_few_args_1(void (*callback)(int, int), int); // expected-error {{'callback' attribute specifies too few arguments}} +__attribute__((callback(1))) void too_few_args_2(int (*callback)(int)); // expected-error {{'callback' attribute specifies too few arguments}} +__attribute__((callback(1, -1))) void too_few_args_3(void (*callback)(int, int)) {} // expected-error {{'callback' attribute specifies too few arguments}} + +__attribute__((callback(0))) void oob_args_0(void (*callback)(void)); // expected-error {{'callback' argument at position 1 is out-of-bounds}} +__attribute__((callback(-1))) void oob_args_1(void (*callback)(void)); // expected-error {{'callback' argument at position 1 is out-of-bounds}} +__attribute__((callback(2))) void oob_args_2(int* (*callback)(void)) {} // expected-error {{'callback' argument at position 1 is out-of-bounds}} +__attribute__((callback(1, 3))) void oob_args_3(short (*callback)(int), int); // expected-error {{'callback' argument at position 2 is out-of-bounds}} +__attribute__((callback(-2, 2))) void oob_args_4(void* (*callback)(int), int); // expected-error {{'callback' argument at position 1 is out-of-bounds}} +__attribute__((callback(1, -2))) void oob_args_5(void* (*callback)(int), int); // expected-error {{'callback' argument at position 2 is out-of-bounds}} +__attribute__((callback(1, 2))) void oob_args_6(void* (*callback)(int), ...); // expected-error {{'callback' argument at position 2 is out-of-bounds}} + +__attribute__((callback(1))) __attribute__((callback(1))) void multiple_cb_1(void (*callback)(void)); // expected-error {{multiple 'callback' attributes specified}} +__attribute__((callback(1))) __attribute__((callback(2))) void multiple_cb_2(void (*callback1)(void), void (*callback2)(void)); // expected-error {{multiple 'callback' attributes specified}} + +#ifndef HAS_THIS +__attribute__((callback(1, 0))) void no_this_1(void* (*callback)(void)); // expected-error {{'callback' argument at position 2 specifies unawailable implicit 'this'}} +__attribute__((callback(1, 0))) void no_this_2(void* (*callback)(int)); // expected-error {{'callback' argument at position 2 specifies unawailable implicit 'this'}} +#endif + +// We could allow the following declarations if we at some point need to: + +__attribute__((callback(1, -1))) void vararg_cb_1(void (*callback)(int, ...)) {} // expected-error {{'callback' callee shall not be variadic}} +__attribute__((callback(1, 1))) void vararg_cb_2(void (*callback)(int, ...), int a); // expected-error {{'callback' callee shall not be variadic}} + +__attribute__((callback(1, -1, 1, 2, 3, 4, -1))) void varargs_1(void (*callback)(int, ...), int a, float b, double c) {} // expected-error {{'callback' attribute specifies too many arguments}} +__attribute__((callback(1, -1, 4, 2, 3, 4, -1))) void varargs_2(void (*callback)(void *, double, int, ...), int a, float b , double c); // expected-error {{'callback' attribute specifies too many arguments}} + +__attribute__((callback(1, -1, 1))) void self_arg_1(void (*callback)(int, ...)) {} // expected-error {{'callback' attribute specifies too many arguments}} +__attribute__((callback(1, -1, 1, -1, -1, 1))) void self_arg_2(void (*callback)(int, ...)); // expected-error {{'callback' attribute specifies too many arguments}} Index: test/Sema/attr-callback.c =================================================================== --- /dev/null +++ test/Sema/attr-callback.c @@ -0,0 +1,8 @@ +// RUN: %clang_cc1 %s -verify -fsyntax-only + +// expected-no-diagnostics + +__attribute__((callback(1))) void no_args(void (*callback)(void)); +__attribute__((callback(1, 2, 3))) void args_1(void (*callback)(int, double), int a, double b); +__attribute__((callback(2, 3, 3))) void args_2(int a, void (*callback)(double, double), double b); +__attribute__((callback(2, -1, -1))) void args_3(int a, void (*callback)(double, double), double b); Index: test/SemaCXX/attr-callback-broken.cpp =================================================================== --- /dev/null +++ test/SemaCXX/attr-callback-broken.cpp @@ -0,0 +1,7 @@ +// RUN: %clang_cc1 %s -verify -fsyntax-only + +class C_in_class { +#define HAS_THIS +#include "../Sema/attr-callback-broken.c" +#undef HAS_THIS +}; Index: test/SemaCXX/attr-callback.cpp =================================================================== --- /dev/null +++ test/SemaCXX/attr-callback.cpp @@ -0,0 +1,67 @@ +// RUN: %clang_cc1 %s -verify -fsyntax-only + +// expected-no-diagnostics + +class C_in_class { +#include "../Sema/attr-callback.c" +}; + +struct Base { + + void no_args_1(void (*callback)(void)); + __attribute__((callback(1))) void no_args_2(void (*callback)(void)); + __attribute__((callback(1))) void no_args_3(void (*callback)(void)) {} + + __attribute__((callback(1, 0))) virtual void + this_tr(void (*callback)(Base *)); + + __attribute__((callback(1, 0, -1, 0))) virtual void + this_unknown_this(void (*callback)(Base *, Base *, Base *)); + + __attribute__((callback(1))) virtual void + virtual_1(void (*callback)(void)); + + __attribute__((callback(1))) virtual void + virtual_2(void (*callback)(void)); + + __attribute__((callback(1))) virtual void + virtual_3(void (*callback)(void)); +}; + +__attribute__((callback(1))) void +Base::no_args_1(void (*callback)(void)) { +} + +void Base::no_args_2(void (*callback)(void)) { +} + +struct Derived_1 : public Base { + + __attribute__((callback(1, 0))) virtual void + this_tr(void (*callback)(Base *)) override; + + __attribute__((callback(1))) virtual void + virtual_1(void (*callback)(void)) override {} + + virtual void + virtual_3(void (*callback)(void)) override {} +}; + +struct Derived_2 : public Base { + + __attribute__((callback(1))) virtual void + virtual_1(void (*callback)(void)) override; + + virtual void + virtual_2(void (*callback)(void)) override; + + virtual void + virtual_3(void (*callback)(void)) override; +}; + +void Derived_2::virtual_1(void (*callback)(void)) {} + +__attribute__((callback(1))) void +Derived_2::virtual_2(void (*callback)(void)) {} + +void Derived_2::virtual_3(void (*callback)(void)) {} Index: utils/TableGen/ClangAttrEmitter.cpp =================================================================== --- utils/TableGen/ClangAttrEmitter.cpp +++ utils/TableGen/ClangAttrEmitter.cpp @@ -1275,6 +1275,8 @@ Ptr = llvm::make_unique(Arg, Attr, "unsigned"); else if (ArgName == "VariadicUnsignedArgument") Ptr = llvm::make_unique(Arg, Attr, "unsigned"); + else if (ArgName == "VariadicSignedArgument") + Ptr = llvm::make_unique(Arg, Attr, "int"); else if (ArgName == "VariadicStringArgument") Ptr = llvm::make_unique(Arg, Attr); else if (ArgName == "VariadicEnumArgument")