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 @@ -190,6 +190,9 @@ // Like VariadicUnsignedArgument except values are ParamIdx. class VariadicParamIdxArgument : Argument; +// A list of identifiers matching parameters or ParamIdx indices. +class VariadicParamOrParamIdxArgument : Argument; + // Like VariadicParamIdxArgument but for a single function parameter index. class ParamIdxArgument : Argument; @@ -1198,6 +1201,13 @@ let Documentation = [Undocumented]; } +def Callback : InheritableAttr { + let Spellings = [Clang<"callback">]; + let Args = [VariadicParamOrParamIdxArgument<"Encoding">]; + 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 @@ -3766,6 +3766,51 @@ }]; } +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 name or position (starting with +1!) in the annotated function. The first position in the attribute identifies +the callback callee, the following positions declare describe its arguments. +The callback callee is required to be callable with the number, and order, of +the specified arguments. The index '0', or the identifier "this", is used to +represent an implicit "this" pointer in class methods. If there is no implicit +"this" pointer it shall not be referenced. The index '-1', or the name "?", +represents an unknown callback callee argument. This can be a value which is +not present in the declared parameter list, or one that is but potentially +inspected, captured, or modified. Parameter names and indices can be mixed in +the callback attribute. + +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. + +Example encodings for the callback performed by `pthread_create` are 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` is automatically recognized by Clang. + + .. code-block:: c + + __attribute__((callback (start_routine, arg))) + int pthread_create(pthread_t *thread, const pthread_attr_t *attr, + void *(*start_routine) (void *), void *arg); + + __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,12 @@ /// 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, add the index to the + /// callback callee argument and the callback payload arguments. + bool performsCallback(unsigned ID, + llvm::SmallVectorImpl &Encoding) 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) @@ -960,6 +961,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 @@ -2568,6 +2568,28 @@ 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 specified">; +def err_callback_attribute_invalid_callee : Error< + "invalid callback callee specified">; +def err_callback_attribute_multiple : Error< + "multiple 'callback' attributes specified">; +def err_callback_attribute_argument_unknown : Error< + "'callback' argument '%0' is not a known function parameter">; +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 references unavailable 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 @@ -9518,6 +9518,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, + SmallVectorImpl &Encoding) 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; + int CalleeIdx = ::strtol(CalleePos, &EndPos, 10); + assert(CalleeIdx >= 0 && "Callee index is supposed to be positive!"); + Encoding.push_back(CalleeIdx); + + while (*EndPos == ',') { + const char *PayloadPos = EndPos + 1; + + int PayloadIdx = ::strtol(PayloadPos, &EndPos, 10); + Encoding.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 @@ -1677,6 +1677,22 @@ 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::LLVMContext &Ctx = F->getContext(); + llvm::MDBuilder MDB(Ctx); + // 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, + *llvm::MDNode::get(Ctx, {MDB.createCallbackEncoding( + 2, {-1, -1}, + /* VarArgsArePassed */ true)})); + } + } break; } case OMPRTL__kmpc_global_thread_num: { @@ -2084,6 +2100,22 @@ 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::LLVMContext &Ctx = F->getContext(); + llvm::MDBuilder MDB(Ctx); + // 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, + *llvm::MDNode::get(Ctx, {MDB.createCallbackEncoding( + 2, {-1, -1}, + /* VarArgsArePassed */ true)})); + } + } break; } case OMPRTL__kmpc_taskloop: { Index: lib/CodeGen/CodeGenModule.cpp =================================================================== --- lib/CodeGen/CodeGenModule.cpp +++ lib/CodeGen/CodeGenModule.cpp @@ -1603,6 +1603,23 @@ 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::LLVMContext &Ctx = F->getContext(); + llvm::MDBuilder MDB(Ctx); + + // The payload indices are all but the first one in the encoding. The first + // identifies the callback callee. + int CalleeIdx = *CB->encoding_begin(); + ArrayRef PayloadIndices(CB->encoding_begin() + 1, CB->encoding_end()); + F->addMetadata(llvm::LLVMContext::MD_callback, + *llvm::MDNode::get(Ctx, {MDB.createCallbackEncoding( + CalleeIdx, PayloadIndices, + /* VarArgsArePassed */ 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,14 @@ FD->getLocation())); } + // Handle automatically recognized callbacks. + SmallVector Encoding; + if (Context.BuiltinInfo.performsCallback(BuiltinID, Encoding)) { + if (!FD->hasAttr()) + FD->addAttr(CallbackAttr::CreateImplicit( + Context, Encoding.data(), Encoding.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 @@ -3462,6 +3462,145 @@ 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. + if (AL.getNumArgs() == 0) { + S.Diag(AL.getLoc(), diag::err_callback_attribute_no_callee) + << AL.getRange(); + return; + } + + bool HasImplicitThisParam = isInstanceMethod(D); + int32_t NumArgs = getFunctionOrMethodNumParams(D); + + FunctionDecl *FD = D->getAsFunction(); + assert(FD && "Expected a function declaration!"); + + llvm::StringMap NameIdxMapping; + NameIdxMapping["__"] = -1; + + NameIdxMapping["__this"] = 0; + + int Idx = 1; + for (const ParmVarDecl *PVD : FD->parameters()) + NameIdxMapping[PVD->getName()] = Idx++; + + auto UnknownName = NameIdxMapping.end(); + + SmallVector EncodingIndices; + for (unsigned u = 0, e = AL.getNumArgs(); u < e; u++) { + + SourceRange SR; + int32_t ArgIdx; + + if (AL.isArgIdent(u)) { + IdentifierLoc *IdLoc = AL.getArgAsIdent(u); + auto It = NameIdxMapping.find(IdLoc->Ident->getName()); + if (It == UnknownName) { + S.Diag(AL.getLoc(), diag::err_callback_attribute_argument_unknown) + << IdLoc->Ident->getName() << IdLoc->Loc; + return; + } + + SR = SourceRange(IdLoc->Loc); + ArgIdx = It->second; + + } else if (AL.isArgExpr(u)) { + Expr *IdxExpr = AL.getArgAsExpr(u); + + // If the expression is not parseable as an int32_t we have a problem. + if (!checkUInt32Argument(S, AL, IdxExpr, (uint32_t &)ArgIdx, u + 1, + false)) { + S.Diag(AL.getLoc(), diag::err_callback_attribute_argument_index_oob) + << (u + 1) << IdxExpr->getSourceRange(); + return; + } + + // Check oob, excluding the special values, 0 and -1. + if (ArgIdx < -1 || ArgIdx > NumArgs) { + S.Diag(AL.getLoc(), diag::err_callback_attribute_argument_index_oob) + << (u + 1) << IdxExpr->getSourceRange(); + return; + } + + SR = IdxExpr->getSourceRange(); + + } else { + llvm_unreachable("Unexpected ParsedAttr argument type!"); + } + + if (ArgIdx == 0 && !HasImplicitThisParam) { + S.Diag(AL.getLoc(), diag::err_callback_implicit_this_not_available) + << (u + 1) << SR; + 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. + if (!HasImplicitThisParam && ArgIdx > 0) + ArgIdx -= 1; + + EncodingIndices.push_back(ArgIdx); + } + + int CalleeIdx = EncodingIndices.front(); + // Check if the callee index is proper, thus not "this" and not "unknown". + if (CalleeIdx < HasImplicitThisParam) { + S.Diag(AL.getLoc(), diag::err_callback_attribute_invalid_callee) + << AL.getRange(); + return; + } + + // Get the callee type, note the index adjustment as the AST doesn't contain + // the this type (which the callee cannot reference anyway!). + const Type *CalleeType = + getFunctionOrMethodParamType(D, CalleeIdx - HasImplicitThisParam) + .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() > EncodingIndices.size() - 1) { + S.Diag(AL.getLoc(), diag::err_callback_too_few_arguments) << AL.getRange(); + return; + } + + if (CalleeFnProtoType->getNumParams() < EncodingIndices.size() - 1) { + 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; + } + + D->addAttr(::new (S.Context) CallbackAttr( + AL.getRange(), S.Context, EncodingIndices.data(), EncodingIndices.size(), + AL.getAttributeSpellingListIndex())); +} + static void handleTransparentUnionAttr(Sema &S, Decl *D, const ParsedAttr &AL) { // Try to find the underlying union declaration. RecordDecl *RD = nullptr; @@ -6366,6 +6505,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 @@ -2,8 +2,8 @@ // RUN: %clang_analyze_cc1 -triple x86_64-apple-darwin10\ // RUN: -analyzer-checker=core,osx.coreFoundation.CFRetainRelease\ // RUN: -analyzer-checker=osx.cocoa.ClassRelease,osx.cocoa.RetainCount\ -// RUN: -analyzer-checker=debug.ExprInspection -fblocks -verify %s\ -// RUN: -Wno-objc-root-class -analyzer-output=plist -o %t.objcpp.plist +// RUN: -analyzer-checker=debug.ExprInspection -fblocks -verify=expected,C\ +// RUN: -Wno-objc-root-class -analyzer-output=plist -o %t.objcpp.plist %s // RUN: %clang_analyze_cc1 -triple x86_64-apple-darwin10\ // RUN: -analyzer-checker=core,osx.coreFoundation.CFRetainRelease\ // RUN: -analyzer-checker=osx.cocoa.ClassRelease,osx.cocoa.RetainCount\ @@ -1202,7 +1202,7 @@ typedef unsigned long __darwin_pthread_key_t; typedef __darwin_pthread_key_t pthread_key_t; -int pthread_create(pthread_t *, const pthread_attr_t *, +int pthread_create(pthread_t *, const pthread_attr_t *, // C-warning{{declaration of built-in function 'pthread_create' requires inclusion of the header }} void *(*)(void *), void *); int pthread_setspecific(pthread_key_t key, const void *value); Index: test/CodeGen/attr-callback.c =================================================================== --- /dev/null +++ test/CodeGen/attr-callback.c @@ -0,0 +1,28 @@ +// 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_2a +__attribute__((callback(2, 3, 3))) void args_2a(int a, void (*callback)(double, double), double b); +// CHECK-DAG: !callback ![[cid2]] void @args_2b +__attribute__((callback(callback, b, b))) void args_2b(int a, void (*callback)(double, double), double b); + +// CHECK-DAG: void @args_3a({{[^#]*#[0-9]+}} !callback ![[cid3:[0-9]+]] +__attribute__((callback(2, -1, -1))) void args_3a(int a, void (*callback)(double, double), double b) { args_2a(a, callback, b); } +// CHECK-DAG: void @args_3b({{[^#]*#[0-9]+}} !callback ![[cid3]] +__attribute__((callback(callback, __, __))) void args_3b(int a, void (*callback)(double, double), double b) { args_2b(a, callback, b); } + +// CHECK-DAG: ![[cid0]] = !{![[cid0b:[0-9]+]]} +// CHECK-DAG: ![[cid0b]] = !{i64 0, i1 false} +// CHECK-DAG: ![[cid1]] = !{![[cid1b:[0-9]+]]} +// CHECK-DAG: ![[cid1b]] = !{i64 0, i64 1, i64 2, i1 false} +// CHECK-DAG: ![[cid2]] = !{![[cid2b:[0-9]+]]} +// CHECK-DAG: ![[cid2b]] = !{i64 1, i64 2, i64 2, i1 false} +// CHECK-DAG: ![[cid3]] = !{![[cid3b:[0-9]+]]} +// CHECK-DAG: ![[cid3b]] = !{i64 1, i64 -1, i64 -1, i1 false} Index: test/CodeGen/callback_annotated.c =================================================================== --- /dev/null +++ test/CodeGen/callback_annotated.c @@ -0,0 +1,76 @@ +// 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 (callee, payload))) +void* broker1(void *payload, void* (*callee)(void *)) { + return broker0(callee, payload); +} + +// RUN1-DAG: declare !callback ![[cid2:[0-9]+]] i8* @broker2 +__attribute__((callback (callee))) +void* broker2(void (*callee)(void)); + +// RUN1-DAG: declare !callback ![[cid3:[0-9]+]] i8* @broker3 +__attribute__((callback (4, 1, 2, c))) +void* broker3(int, int, int c, int (*callee)(int, int, int), int); + +// RUN1-DAG: declare !callback ![[cid4:[0-9]+]] i8* @broker4 +__attribute__((callback (4, -1, a, __))) +void* broker4(int a, int, int, int (*callee)(int, int, int), int); + +// RUN1-DAG: declare !callback ![[cid5:[0-9]+]] i8* @broker5 +__attribute__((callback (4, d, 5, 2))) +void* broker5(int, int, int, int (*callee)(int, int, int), int d); + + +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]] = !{![[cid0b:[0-9]+]]} +// RUN1-DAG: ![[cid0b]] = !{i64 0, i64 1, i1 false} +// RUN1-DAG: ![[cid1]] = !{![[cid1b:[0-9]+]]} +// RUN1-DAG: ![[cid1b]] = !{i64 1, i64 0, i1 false} +// RUN1-DAG: ![[cid2]] = !{![[cid2b:[0-9]+]]} +// RUN1-DAG: ![[cid2b]] = !{i64 0, i1 false} +// RUN1-DAG: ![[cid3]] = !{![[cid3b:[0-9]+]]} +// RUN1-DAG: ![[cid3b]] = !{i64 3, i64 0, i64 1, i64 2, i1 false} +// RUN1-DAG: ![[cid4]] = !{![[cid4b:[0-9]+]]} +// RUN1-DAG: ![[cid4b]] = !{i64 3, i64 -1, i64 0, i64 -1, i1 false} +// RUN1-DAG: ![[cid5]] = !{![[cid5b:[0-9]+]]} +// RUN1-DAG: ![[cid5b]] = !{i64 3, i64 4, i64 4, i64 1, i1 false} Index: test/CodeGen/callback_openmp.c =================================================================== --- /dev/null +++ test/CodeGen/callback_openmp.c @@ -0,0 +1,28 @@ +// 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]] = !{![[cidb:[0-9]+]]} +// CHECK: ![[cidb]] = !{i64 2, i64 -1, i64 -1, i1 true} + +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,32 @@ +// 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]] = !{![[cidb:[0-9]+]]} +// CHECK: ![[cidb]] = !{i64 2, i64 3, i1 false} + +#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/CodeGenCXX/attr-callback.cpp =================================================================== --- /dev/null +++ test/CodeGenCXX/attr-callback.cpp @@ -0,0 +1,55 @@ +// 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)); + __attribute__((callback(callback1))) void no_args_3(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(callback, __this, __, __this))) 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 @_ZN4Base9no_args_3EPFvvES1_({{[^!]*!callback}} ![[cid1]] +__attribute__((callback(callback2))) void Base::no_args_3(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]] = !{![[cid0b:[0-9]+]]} +// CHECK-DAG: ![[cid0b]] = !{i64 1, i1 false} +// CHECK-DAG: ![[cid1]] = !{![[cid1b:[0-9]+]]} +// CHECK-DAG: ![[cid1b]] = !{i64 2, i1 false} +// CHECK-DAG: ![[cid2]] = !{![[cid2b:[0-9]+]]} +// CHECK-DAG: ![[cid2b]] = !{i64 1, i64 0, i64 -1, i64 0, i1 false} 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,6 @@ // CHECK: attributes #[[FN_ATTRS]] = {{.+}} nounwind // CHECK-DEBUG: attributes #[[FN_ATTRS]] = {{.+}} nounwind - +// CHECK: ![[cbid]] = !{![[cbidb:[0-9]+]]} +// CHECK: ![[cbidb]] = !{i64 2, i64 -1, i64 -1, i1 true} #endif Index: test/Sema/attr-callback-broken.c =================================================================== --- /dev/null +++ test/Sema/attr-callback-broken.c @@ -0,0 +1,75 @@ +// RUN: %clang_cc1 %s -verify -fsyntax-only + +__attribute__((callback())) void no_callee(void (*callback)(void)); // expected-error {{no callback callee specified}} + +__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(-1))) void oob_args_1(void (*callback)(void)); // expected-error {{invalid callback callee specified}} +__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}} + +#ifdef HAS_THIS +__attribute__((callback(0))) void oob_args_0(void (*callback)(void)); // expected-error {{invalid callback callee specified}} +#else +__attribute__((callback(0))) void oob_args_0(void (*callback)(void)); // expected-error {{'callback' argument at position 1 references unavailable implicit 'this'}} +__attribute__((callback(1, 0))) void no_this_1(void* (*callback)(void*)); // expected-error {{'callback' argument at position 2 references unavailable implicit 'this'}} +__attribute__((callback(1, 0))) void no_this_2(void* (*callback)(int, void*)); // expected-error {{'callback' argument at position 2 references unavailable 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}} + +__attribute__((callback(cb))) void unknown_name1(void (*callback)(void)) {} // expected-error {{'callback' argument 'cb' is not a known function parameter}} +__attribute__((callback(cb, ab))) void unknown_name2(void (*cb)(int), int a) {} // expected-error {{'callback' argument 'ab' is not a known function parameter}} + +__attribute__((callback(callback, 1))) void too_many_args_1b(void (*callback)(void)) {} // expected-error {{'callback' attribute specifies too many arguments}} +__attribute__((callback(callback, __))) void too_many_args_2b(double (*callback)(void)); // expected-error {{'callback' attribute specifies too many arguments}} +__attribute__((callback(callback, 2, 2))) void too_many_args_3b(void (*callback)(int), int); // expected-error {{'callback' attribute specifies too many arguments}} + +__attribute__((callback(callback, a))) void too_few_args_1b(void (*callback)(int, int), int a); // expected-error {{'callback' attribute specifies too few arguments}} +__attribute__((callback(callback))) void too_few_args_2b(int (*callback)(int)); // expected-error {{'callback' attribute specifies too few arguments}} +__attribute__((callback(callback, __))) void too_few_args_3b(void (*callback)(int, int)) {} // expected-error {{'callback' attribute specifies too few arguments}} + +__attribute__((callback(__))) void oob_args_1b(void (*callback)(void)); // expected-error {{invalid callback callee specified}} + +__attribute__((callback(callback))) __attribute__((callback(callback))) void multiple_cb_1b(void (*callback)(void)); // expected-error {{multiple 'callback' attributes specified}} +__attribute__((callback(1))) __attribute__((callback(callback2))) void multiple_cb_2b(void (*callback1)(void), void (*callback2)(void)); // expected-error {{multiple 'callback' attributes specified}} + +#ifdef HAS_THIS +__attribute__((callback(__this))) void oob_args_0b(void (*callback)(void)); // expected-error {{invalid callback callee specified}} +#else +__attribute__((callback(__this))) void oob_args_0b(void (*callback)(void)); // expected-error {{'callback' argument at position 1 references unavailable implicit 'this'}} +__attribute__((callback(1, __this))) void no_this_1b(void* (*callback)(void*)); // expected-error {{'callback' argument at position 2 references unavailable implicit 'this'}} +__attribute__((callback(1, __this))) void no_this_2b(void* (*callback)(int, void*)); // expected-error {{'callback' argument at position 2 references unavailable implicit 'this'}} +#endif + +// We could allow the following declarations if we at some point need to: + +__attribute__((callback(callback, __))) void vararg_cb_1b(void (*callback)(int, ...)) {} // expected-error {{'callback' callee shall not be variadic}} +__attribute__((callback(1, a))) void vararg_cb_2b(void (*callback)(int, ...), int a); // expected-error {{'callback' callee shall not be variadic}} + +__attribute__((callback(callback, __, callback, a, b, c, __))) void varargs_1b(void (*callback)(int, ...), int a, float b, double c) {} // expected-error {{'callback' attribute specifies too many arguments}} +__attribute__((callback(1, __, c, a, b, c, -1))) void varargs_2b(void (*callback)(void *, double, int, ...), int a, float b , double c); // expected-error {{'callback' attribute specifies too many arguments}} + +__attribute__((callback(1, __, callback))) void self_arg_1b(void (*callback)(int, ...)) {} // expected-error {{'callback' attribute specifies too many arguments}} +__attribute__((callback(callback, __, callback, __, __, callback))) void self_arg_2b(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,14 @@ +// 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); + +__attribute__((callback(callback))) void no_argsb(void (*callback)(void)); +__attribute__((callback(callback, a, 3))) void args_1b(void (*callback)(int, double), int a, double b); +__attribute__((callback(callback, b, b))) void args_2b(int a, void (*callback)(double, double), double b); +__attribute__((callback(2, __, __))) void args_3b(int a, void (*callback)(double, double), double b); +__attribute__((callback(callback, -1, __))) void args_3c(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(callback))) void no_args_3(void (*callback)(void)) {} + + __attribute__((callback(1, 0))) virtual void + this_tr(void (*callback)(Base *)); + + __attribute__((callback(1, __this, __, __this))) virtual void + this_unknown_this(void (*callback)(Base *, Base *, Base *)); + + __attribute__((callback(1))) virtual void + virtual_1(void (*callback)(void)); + + __attribute__((callback(callback))) 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(callback))) 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 @@ -775,6 +775,11 @@ } }; + struct VariadicParamOrParamIdxArgument : public VariadicArgument { + VariadicParamOrParamIdxArgument(const Record &Arg, StringRef Attr) + : VariadicArgument(Arg, Attr, "int") {} + }; + // Unique the enums, but maintain the original declaration ordering. std::vector uniqueEnumsInOrder(const std::vector &enums) { @@ -1283,6 +1288,8 @@ Ptr = llvm::make_unique(Arg, Attr); else if (ArgName == "VariadicParamIdxArgument") Ptr = llvm::make_unique(Arg, Attr); + else if (ArgName == "VariadicParamOrParamIdxArgument") + Ptr = llvm::make_unique(Arg, Attr); else if (ArgName == "ParamIdxArgument") Ptr = llvm::make_unique(Arg, Attr, "ParamIdx"); else if (ArgName == "VariadicIdentifierArgument") @@ -2116,6 +2123,7 @@ llvm::StringSwitch( Arg->getSuperClasses().back().first->getName()) .Case("VariadicIdentifierArgument", true) + .Case("VariadicParamOrParamIdxArgument", true) .Default(false); }