jdoerfert updated this revision to Diff 180714.
jdoerfert added a comment.
Fix tests
Repository:
rC Clang
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D55483/new/
https://reviews.llvm.org/D55483
Files:
include/clang/AST/ASTContext.h
include/clang/Basic/Attr.td
include/clang/Basic/AttrDocs.td
include/clang/Basic/Builtins.def
include/clang/Basic/Builtins.h
include/clang/Basic/DiagnosticSemaKinds.td
lib/AST/ASTContext.cpp
lib/Basic/Builtins.cpp
lib/CodeGen/CGOpenMPRuntime.cpp
lib/CodeGen/CodeGenModule.cpp
lib/Sema/SemaDecl.cpp
lib/Sema/SemaDeclAttr.cpp
test/Analysis/retain-release.m
test/CodeGen/attr-callback.c
test/CodeGen/callback_annotated.c
test/CodeGen/callback_openmp.c
test/CodeGen/callback_pthread_create.c
test/CodeGenCXX/attr-callback.cpp
test/Misc/pragma-attribute-supported-attributes-list.test
test/OpenMP/parallel_codegen.cpp
test/Sema/attr-callback-broken.c
test/Sema/attr-callback.c
test/SemaCXX/attr-callback-broken.cpp
test/SemaCXX/attr-callback.cpp
utils/TableGen/ClangAttrEmitter.cpp
Index: utils/TableGen/ClangAttrEmitter.cpp
===================================================================
--- utils/TableGen/ClangAttrEmitter.cpp
+++ utils/TableGen/ClangAttrEmitter.cpp
@@ -1275,6 +1275,8 @@
Ptr = llvm::make_unique<SimpleArgument>(Arg, Attr, "unsigned");
else if (ArgName == "VariadicUnsignedArgument")
Ptr = llvm::make_unique<VariadicArgument>(Arg, Attr, "unsigned");
+ else if (ArgName == "VariadicSignedArgument")
+ Ptr = llvm::make_unique<VariadicArgument>(Arg, Attr, "int");
else if (ArgName == "VariadicStringArgument")
Ptr = llvm::make_unique<VariadicStringArgument>(Arg, Attr);
else if (ArgName == "VariadicEnumArgument")
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: 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/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/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/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/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/CodeGenCXX/attr-callback.cpp
===================================================================
--- /dev/null
+++ test/CodeGenCXX/attr-callback.cpp
@@ -0,0 +1,51 @@
+// 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]] = !{![[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/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 <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,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_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 (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]] = !{![[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/attr-callback.c
===================================================================
--- /dev/null
+++ test/CodeGen/attr-callback.c
@@ -0,0 +1,24 @@
+// 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]] = !{![[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/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 <pthread.h>}}
void *(*)(void *), void *);
int pthread_setspecific(pthread_key_t key, const void *value);
Index: lib/Sema/SemaDeclAttr.cpp
===================================================================
--- lib/Sema/SemaDeclAttr.cpp
+++ lib/Sema/SemaDeclAttr.cpp
@@ -3462,6 +3462,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<int, 8> 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<FunctionProtoType>(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<CallbackAttr>()) {
+ 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;
@@ -6366,6 +6463,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
@@ -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,17 @@
FD->getLocation()));
}
+ // Hadnle automatically recognized callbacks.
+ unsigned CallbackCalleeIdx;
+ SmallVector<int, 4> CallbackPayloadIndices;
+ if (Context.BuiltinInfo.performsCallback(BuiltinID, CallbackCalleeIdx,
+ CallbackPayloadIndices)) {
+ if (!FD->hasAttr<CallbackAttr>())
+ 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/CodeGen/CodeGenModule.cpp
===================================================================
--- lib/CodeGen/CodeGenModule.cpp
+++ lib/CodeGen/CodeGenModule.cpp
@@ -1603,6 +1603,21 @@
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::LLVMContext &Ctx = F->getContext();
+ llvm::MDBuilder MDB(Ctx);
+ ArrayRef<int> PayloadIndices(CB->payloadIndices_begin(),
+ CB->payloadIndices_size());
+ F->addMetadata(
+ llvm::LLVMContext::MD_callback,
+ *llvm::MDNode::get(
+ Ctx, {MDB.createCallbackEncoding(CB->getCalleeIdx(), PayloadIndices,
+ /* VarArgsArePassed */ false)}));
+ }
}
void CodeGenModule::addUsedGlobal(llvm::GlobalValue *GV) {
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<llvm::Function>(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<llvm::Function>(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/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<int> &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/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<QualType, 8> ArgTypes;
Index: include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- include/clang/Basic/DiagnosticSemaKinds.td
+++ include/clang/Basic/DiagnosticSemaKinds.td
@@ -2568,6 +2568,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: 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<int> &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<N,M_0,...,M_k> -> 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/AttrDocs.td
===================================================================
--- include/clang/Basic/AttrDocs.td
+++ include/clang/Basic/AttrDocs.td
@@ -3766,6 +3766,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 <http://llvm.org/docs/LangRef.html#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/Attr.td
===================================================================
--- include/clang/Basic/Attr.td
+++ include/clang/Basic/Attr.td
@@ -183,6 +183,7 @@
class TypeArgument<string name, bit opt = 0> : Argument<name, opt>;
class UnsignedArgument<string name, bit opt = 0> : Argument<name, opt>;
class VariadicUnsignedArgument<string name> : Argument<name, 1>;
+class VariadicSignedArgument<string name> : Argument<name, 1>;
class VariadicExprArgument<string name> : Argument<name, 1>;
class VariadicStringArgument<string name> : Argument<name, 1>;
class VariadicIdentifierArgument<string name> : Argument<name, 1>;
@@ -1198,6 +1199,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/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 <stdio.h>
GE_Missing_stdio,
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits