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
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to