jdoerfert created this revision. jdoerfert added reviewers: hfinkel, reames, fhahn, ABataev. Herald added subscribers: jfb, bollu.
With commit rXXXXX (currently https://reviews.llvm.org/D54498), LLVM gained the ability to apply existing optimizations on indirections through callbacks. This is based on an abstraction that hides the middle man as described in rXXXXX and the llvm::AbstractCallSite class. This commit enables clang to emit !callback metadata that is understood by LLVM. It does so in three different cases: 1) For known broker functions declarations that are directly emitted, e.g., __kmpc_fork_call for the OpenMP pragma parallel. 2) For known broker functions that are identified by their name and source location through the builtin mechanism, e.g., pthread_create from the POSIX thread API. 3) For user annotated functions that carry the "callback(idx, ...)" attribute. The attribute has to include the index of the callback callee and how the passed arguments can be identified (as many as the callback callee has). For additional information, also consider the commit message and discussion for the LLVM patch: https://reviews.llvm.org/D54498 NOTE: This is only committed after https://reviews.llvm.org/D54498 and the commit message will be modified accordingly. Repository: rC Clang https://reviews.llvm.org/D55483 Files: include/clang/Basic/Attr.td include/clang/Basic/Builtins.def include/clang/Basic/Builtins.h include/clang/Basic/DiagnosticSemaKinds.td lib/Basic/Builtins.cpp lib/CodeGen/CGOpenMPRuntime.cpp lib/CodeGen/CodeGenModule.cpp lib/Sema/SemaDecl.cpp lib/Sema/SemaDeclAttr.cpp test/CodeGen/callback_annotated.c test/CodeGen/callback_openmp.c test/CodeGen/callback_pthread_create.c
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 3, i64 4} + +#include <pthread.h> + +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_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 3, i64 0, i64 0} + +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_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, 0, 1, 0))) +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 1, i64 2} +// RUN1-DAG: ![[cid1]] = !{i1 false, i64 2, i64 1} +// RUN1-DAG: ![[cid2]] = !{i1 false, i64 1} +// RUN1-DAG: ![[cid3]] = !{i1 false, i64 4, i64 1, i64 2, i64 3} +// RUN1-DAG: ![[cid4]] = !{i1 false, i64 4, i64 0, i64 1, i64 0} +// RUN1-DAG: ![[cid5]] = !{i1 false, i64 4, i64 5, i64 5, i64 2} Index: lib/Sema/SemaDeclAttr.cpp =================================================================== --- lib/Sema/SemaDeclAttr.cpp +++ lib/Sema/SemaDeclAttr.cpp @@ -3455,6 +3455,64 @@ D->addAttr(NewAttr); } +/// Handle __attribute__((callback(CalleeIdx, PayloadIdx0, ...))) attributes. +static void handleCallbackAttr(Sema &S, Decl *D, const ParsedAttr &AL) { + // In C++ the implicit 'this' function parameter also counts, and they are + // counted from one. + bool HasImplicitThisParam = isInstanceMethod(D); + unsigned NumArgs = getFunctionOrMethodNumParams(D) + HasImplicitThisParam; + + // Require an index that identifies the callback callee. + if (AL.getNumArgs() == 0) { + S.Diag(AL.getLoc(), diag::err_callback_attribute_no_callee) + << D->getSourceRange(); + return; + } + + // Helper to extract a number and validate its range. + auto ExtractArgIdx = [&](unsigned Pos, unsigned Min) -> int { + Expr *IdxExpr = AL.getArgAsExpr(Pos); + uint32_t Idx; + if (!checkUInt32Argument(S, AL, IdxExpr, Idx, Pos + 1, true)) + return -1; + + if (Idx < Min || Idx > NumArgs) { + S.Diag(AL.getLoc(), diag::err_attribute_argument_out_of_bounds) + << AL << (Pos + 1) << IdxExpr->getSourceRange(); + return -1; + } + // Adjust for an implicit "this" argument. + return Idx + HasImplicitThisParam; + }; + + // If the callee index is 0 it is invalid (we start counting with 1). + int CalleeIdx = ExtractArgIdx(0, 1); + if (CalleeIdx == -1) + return; + + SmallVector<unsigned, 8> PayloadIndices; + for (unsigned i = 1, e = AL.getNumArgs(); i < e; i++) { + // A payload index can be 0 to indicate an unknown value. + int PayloadIdx = ExtractArgIdx(i, 0); + if (PayloadIdx == -1) + return; + PayloadIndices.push_back(PayloadIdx); + } + + // TODO: Check the type of the callee argument and if the number of unknown + // and forwarded arguments matches the type. + + // Do not allow multiple callback attributes. + if (D->hasAttr<CallbackAttr>()) { + S.Diag(AL.getLoc(), diag::err_callback_attribute_multiple) << AL.getRange(); + return; + } + + 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 +6330,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: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -13578,6 +13578,17 @@ FD->getLocation())); } + // Automatically recognized callbacks are currently limited to a single + // payload argument. + unsigned CallbackCalleeIdx, CallbackPayloadIdx; + if (Context.BuiltinInfo.performsCallback(BuiltinID, CallbackCalleeIdx, + CallbackPayloadIdx)) { + if (!FD->hasAttr<CallbackAttr>()) + FD->addAttr(CallbackAttr::CreateImplicit(Context, CallbackCalleeIdx, + &CallbackPayloadIdx, 1, + 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/CodeGen/CodeGenModule.cpp =================================================================== --- lib/CodeGen/CodeGenModule.cpp +++ lib/CodeGen/CodeGenModule.cpp @@ -1600,6 +1600,18 @@ if (getLangOpts().OpenMP && FD->hasAttr<OMPDeclareSimdDeclAttr>()) getOpenMPRuntime().emitDeclareSimdFunction(FD, F); + + if (const auto *CB = FD->getAttr<CallbackAttr>()) { + // Annotate the callback behavior as metadata: + // - The callback callee (as argument number). + // - The callback payloads (as argument numbers). + llvm::MDBuilder MDB(F->getContext()); + ArrayRef<unsigned> 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/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<llvm::Function>(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(3, {0, 0}, /* 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<llvm::Function>(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(3, {0, 0}, /* VarArg */ true)); + } + } break; } case OMPRTL__kmpc_taskloop: { Index: lib/Basic/Builtins.cpp =================================================================== --- lib/Basic/Builtins.cpp +++ lib/Basic/Builtins.cpp @@ -156,6 +156,29 @@ return isLike(ID, FormatIdx, HasVAListArg, "sS"); } +bool Builtin::Context::performsCallback(unsigned ID, unsigned &CalleeIdx, + unsigned &PayloadIdx) 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); + assert(*EndPos == ':' && "Callback callee specifier must end with a ':'"); + + CalleePos = EndPos + 1; + + PayloadIdx = ::strtol(CalleePos, &EndPos, 10); + assert(*EndPos == ':' && "Callback payload specifier must end with a ':'"); + + return true; +} + bool Builtin::Context::canBeRedeclared(unsigned ID) const { return ID == Builtin::NotBuiltin || ID == Builtin::BI__va_start || Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -2588,6 +2588,10 @@ 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 found">; 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: 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, set the index to the + /// callback callee argument and the callback payload argument. + bool performsCallback(unsigned ID, unsigned &CalleeIdx, + unsigned &PayloadIdx) 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:N:M: -> callback behavior: argument N is called with argument M as payload // FIXME: gcc has nonnull #if defined(BUILTIN) && !defined(LIBBUILTIN) @@ -956,6 +957,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, "iv*vC*v*v*", "fC:3:4:", "pthread.h", ALL_GNU_LANGUAGES) + // POSIX setjmp.h LIBBUILTIN(_setjmp, "iJ", "fj", "setjmp.h", ALL_LANGUAGES) Index: include/clang/Basic/Attr.td =================================================================== --- include/clang/Basic/Attr.td +++ include/clang/Basic/Attr.td @@ -1197,6 +1197,14 @@ let Documentation = [Undocumented]; } +def Callback : InheritableAttr { + let Spellings = [Clang<"callback">]; + let Args = [UnsignedArgument<"CalleeIdx">, + VariadicUnsignedArgument<"PayloadIndices">]; + let Subjects = SubjectList<[Function]>; + let Documentation = [Undocumented]; +} + def GNUInline : InheritableAttr { let Spellings = [GCC<"gnu_inline">]; let Subjects = SubjectList<[Function]>;
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits