summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Smith <richard@metafoo.co.uk>2020-09-29 15:20:11 -0700
committerTom Stellard <tstellar@redhat.com>2020-12-14 19:43:12 -0500
commit0312bec0d7573b0549a222fb5d0a695fcf819dc3 (patch)
tree3e773b169d2fe3efbbc5504fdf85272441d94121
parentfdab756331f322a9818c1bdf14d23d9cd6036c81 (diff)
downloadllvm-0312bec0d7573b0549a222fb5d0a695fcf819dc3.tar.gz
Recognize setjmp and friends as builtins even if jmp_buf is not declared yet.
This happens in glibc's headers. It's important that we recognize these functions so that we can mark them as returns_twice. Differential Revision: https://reviews.llvm.org/D88518
-rw-r--r--clang/include/clang/Basic/Builtins.def25
-rw-r--r--clang/include/clang/Basic/Builtins.h7
-rw-r--r--clang/lib/Sema/SemaDecl.cpp23
-rw-r--r--clang/test/CodeGen/setjmp.c44
-rw-r--r--clang/test/Sema/builtin-setjmp.c44
-rw-r--r--clang/test/Sema/implicit-builtin-decl.c5
6 files changed, 116 insertions, 32 deletions
diff --git a/clang/include/clang/Basic/Builtins.def b/clang/include/clang/Basic/Builtins.def
index 01c28ebab763..dbb5dde20a2b 100644
--- a/clang/include/clang/Basic/Builtins.def
+++ b/clang/include/clang/Basic/Builtins.def
@@ -75,6 +75,9 @@
// U -> pure
// c -> const
// t -> signature is meaningless, use custom typechecking
+// T -> type is not important to semantic analysis and codegen; recognize as
+// builtin even if type doesn't match signature, and don't warn if we
+// can't be sure the type is right
// F -> this is a libc/libm function with a '__builtin_' prefix added.
// f -> this is a libc/libm function without the '__builtin_' prefix. It can
// be followed by ':headername:' to state which header this function
@@ -893,7 +896,7 @@ LANGBUILTIN(__va_start, "vc**.", "nt", ALL_MS_LANGUAGES)
LANGBUILTIN(__fastfail, "vUi", "nr", ALL_MS_LANGUAGES)
// Microsoft library builtins.
-LIBBUILTIN(_setjmpex, "iJ", "fj", "setjmpex.h", ALL_MS_LANGUAGES)
+LIBBUILTIN(_setjmpex, "iJ", "fjT", "setjmpex.h", ALL_MS_LANGUAGES)
// C99 library functions
// C99 stdarg.h
@@ -987,8 +990,8 @@ LIBBUILTIN(wmemmove,"w*w*wC*z", "f", "wchar.h", ALL_LANGUAGES)
// In some systems setjmp is a macro that expands to _setjmp. We undefine
// it here to avoid having two identical LIBBUILTIN entries.
#undef setjmp
-LIBBUILTIN(setjmp, "iJ", "fj", "setjmp.h", ALL_LANGUAGES)
-LIBBUILTIN(longjmp, "vJi", "fr", "setjmp.h", ALL_LANGUAGES)
+LIBBUILTIN(setjmp, "iJ", "fjT", "setjmp.h", ALL_LANGUAGES)
+LIBBUILTIN(longjmp, "vJi", "frT", "setjmp.h", ALL_LANGUAGES)
// Non-C library functions, active in GNU mode only.
// Functions with (returns_twice) attribute (marked as "j") are still active in
@@ -1015,21 +1018,21 @@ LIBBUILTIN(strcasecmp, "icC*cC*", "f", "strings.h", ALL_GNU_LANGUAGES)
LIBBUILTIN(strncasecmp, "icC*cC*z", "f", "strings.h", ALL_GNU_LANGUAGES)
// POSIX unistd.h
LIBBUILTIN(_exit, "vi", "fr", "unistd.h", ALL_GNU_LANGUAGES)
-LIBBUILTIN(vfork, "p", "fj", "unistd.h", ALL_LANGUAGES)
+LIBBUILTIN(vfork, "p", "fjT", "unistd.h", ALL_LANGUAGES)
// POSIX pthread.h
// FIXME: Should specify argument types.
LIBBUILTIN(pthread_create, "", "fC<2,3>", "pthread.h", ALL_GNU_LANGUAGES)
// POSIX setjmp.h
-LIBBUILTIN(_setjmp, "iJ", "fj", "setjmp.h", ALL_LANGUAGES)
-LIBBUILTIN(__sigsetjmp, "iSJi", "fj", "setjmp.h", ALL_LANGUAGES)
-LIBBUILTIN(sigsetjmp, "iSJi", "fj", "setjmp.h", ALL_LANGUAGES)
-LIBBUILTIN(savectx, "iJ", "fj", "setjmp.h", ALL_LANGUAGES)
-LIBBUILTIN(getcontext, "iK*", "fj", "setjmp.h", ALL_LANGUAGES)
+LIBBUILTIN(_setjmp, "iJ", "fjT", "setjmp.h", ALL_LANGUAGES)
+LIBBUILTIN(__sigsetjmp, "iSJi", "fjT", "setjmp.h", ALL_LANGUAGES)
+LIBBUILTIN(sigsetjmp, "iSJi", "fjT", "setjmp.h", ALL_LANGUAGES)
+LIBBUILTIN(savectx, "iJ", "fjT", "setjmp.h", ALL_LANGUAGES)
+LIBBUILTIN(getcontext, "iK*", "fjT", "setjmp.h", ALL_LANGUAGES)
-LIBBUILTIN(_longjmp, "vJi", "fr", "setjmp.h", ALL_GNU_LANGUAGES)
-LIBBUILTIN(siglongjmp, "vSJi", "fr", "setjmp.h", ALL_GNU_LANGUAGES)
+LIBBUILTIN(_longjmp, "vJi", "frT", "setjmp.h", ALL_GNU_LANGUAGES)
+LIBBUILTIN(siglongjmp, "vSJi", "frT", "setjmp.h", ALL_GNU_LANGUAGES)
// non-standard but very common
LIBBUILTIN(strlcpy, "zc*cC*z", "f", "string.h", ALL_GNU_LANGUAGES)
LIBBUILTIN(strlcat, "zc*cC*z", "f", "string.h", ALL_GNU_LANGUAGES)
diff --git a/clang/include/clang/Basic/Builtins.h b/clang/include/clang/Basic/Builtins.h
index e4ed482d9068..15bfcf797917 100644
--- a/clang/include/clang/Basic/Builtins.h
+++ b/clang/include/clang/Basic/Builtins.h
@@ -158,6 +158,13 @@ public:
return strchr(getRecord(ID).Attributes, 't') != nullptr;
}
+ /// Determines whether a declaration of this builtin should be recognized
+ /// even if the type doesn't match the specified signature.
+ bool allowTypeMismatch(unsigned ID) const {
+ return strchr(getRecord(ID).Attributes, 'T') != nullptr ||
+ hasCustomTypechecking(ID);
+ }
+
/// Determines whether this builtin has a result or any arguments which
/// are pointer types.
bool hasPtrArgsOrResult(unsigned ID) const {
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 82c78e3003f0..5b0417fa8859 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -2107,7 +2107,8 @@ NamedDecl *Sema::LazilyCreateBuiltin(IdentifierInfo *II, unsigned ID,
// If we have a builtin without an associated type we should not emit a
// warning when we were not able to find a type for it.
- if (Error == ASTContext::GE_Missing_type)
+ if (Error == ASTContext::GE_Missing_type ||
+ Context.BuiltinInfo.allowTypeMismatch(ID))
return nullptr;
// If we could not find a type for setjmp it is because the jmp_buf type was
@@ -2131,11 +2132,9 @@ NamedDecl *Sema::LazilyCreateBuiltin(IdentifierInfo *II, unsigned ID,
Context.BuiltinInfo.isHeaderDependentFunction(ID))) {
Diag(Loc, diag::ext_implicit_lib_function_decl)
<< Context.BuiltinInfo.getName(ID) << R;
- if (Context.BuiltinInfo.getHeaderName(ID) &&
- !Diags.isIgnored(diag::ext_implicit_lib_function_decl, Loc))
+ if (const char *Header = Context.BuiltinInfo.getHeaderName(ID))
Diag(Loc, diag::note_include_header_or_declare)
- << Context.BuiltinInfo.getHeaderName(ID)
- << Context.BuiltinInfo.getName(ID);
+ << Header << Context.BuiltinInfo.getName(ID);
}
if (R.isNull())
@@ -9630,16 +9629,16 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
}
}
- // In C builtins get merged with implicitly lazily created declarations.
- // In C++ we need to check if it's a builtin and add the BuiltinAttr here.
- if (getLangOpts().CPlusPlus) {
+ // If this is the first declaration of a library builtin function, add
+ // attributes as appropriate.
+ if (!D.isRedeclaration() &&
+ NewFD->getDeclContext()->getRedeclContext()->isFileContext()) {
if (IdentifierInfo *II = Previous.getLookupName().getAsIdentifierInfo()) {
if (unsigned BuiltinID = II->getBuiltinID()) {
if (NewFD->getLanguageLinkage() == CLanguageLinkage) {
- // Declarations for builtins with custom typechecking by definition
- // don't make sense. Don't attempt typechecking and simply add the
- // attribute.
- if (Context.BuiltinInfo.hasCustomTypechecking(BuiltinID)) {
+ // Validate the type matches unless this builtin is specified as
+ // matching regardless of its declared type.
+ if (Context.BuiltinInfo.allowTypeMismatch(BuiltinID)) {
NewFD->addAttr(BuiltinAttr::CreateImplicit(Context, BuiltinID));
} else {
ASTContext::GetBuiltinTypeError Error;
diff --git a/clang/test/CodeGen/setjmp.c b/clang/test/CodeGen/setjmp.c
new file mode 100644
index 000000000000..4ca360d8584c
--- /dev/null
+++ b/clang/test/CodeGen/setjmp.c
@@ -0,0 +1,44 @@
+// RUN: %clang_cc1 -x c %s -triple x86_64-linux-gnu -emit-llvm -o - | FileCheck %s
+// RUN: %clang_cc1 -x c++ %s -triple x86_64-linux-gnu -emit-llvm -o - | FileCheck %s
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct __jmp_buf_tag { int n; };
+int setjmp(struct __jmp_buf_tag*);
+int sigsetjmp(struct __jmp_buf_tag*, int);
+int _setjmp(struct __jmp_buf_tag*);
+int __sigsetjmp(struct __jmp_buf_tag*, int);
+
+typedef struct __jmp_buf_tag jmp_buf[1];
+typedef struct __jmp_buf_tag sigjmp_buf[1];
+
+#ifdef __cplusplus
+}
+#endif
+
+void f() {
+ jmp_buf jb;
+ // CHECK: call {{.*}}@setjmp(
+ setjmp(jb);
+ // CHECK: call {{.*}}@sigsetjmp(
+ sigsetjmp(jb, 0);
+ // CHECK: call {{.*}}@_setjmp(
+ _setjmp(jb);
+ // CHECK: call {{.*}}@__sigsetjmp(
+ __sigsetjmp(jb, 0);
+}
+
+// CHECK: ; Function Attrs: returns_twice
+// CHECK-NEXT: declare {{.*}} @setjmp(
+
+// CHECK: ; Function Attrs: returns_twice
+// CHECK-NEXT: declare {{.*}} @sigsetjmp(
+
+// CHECK: ; Function Attrs: returns_twice
+// CHECK-NEXT: declare {{.*}} @_setjmp(
+
+// CHECK: ; Function Attrs: returns_twice
+// CHECK-NEXT: declare {{.*}} @__sigsetjmp(
+
diff --git a/clang/test/Sema/builtin-setjmp.c b/clang/test/Sema/builtin-setjmp.c
index f8770d88e731..6a114fad05d9 100644
--- a/clang/test/Sema/builtin-setjmp.c
+++ b/clang/test/Sema/builtin-setjmp.c
@@ -1,10 +1,42 @@
-// RUN: %clang_cc1 -triple x86_64-unknown-unknown -fsyntax-only -verify -DNO_JMP_BUF %s
-// RUN: %clang_cc1 -triple x86_64-unknown-unknown -fsyntax-only -verify %s
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown -fsyntax-only -verify -DNO_JMP_BUF %s -ast-dump | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown -fsyntax-only -verify -DWRONG_JMP_BUF %s -ast-dump | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown -fsyntax-only -verify -DRIGHT_JMP_BUF %s -ast-dump | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown -fsyntax-only -verify -DONLY_JMP_BUF %s -ast-dump | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown -fsyntax-only -verify -DNO_SETJMP %s -ast-dump 2>&1 | FileCheck %s
#ifdef NO_JMP_BUF
-extern long setjmp(long *); // expected-warning {{declaration of built-in function 'setjmp' requires the declaration of the 'jmp_buf' type, commonly provided in the header <setjmp.h>.}}
-#else
+// This happens in some versions of glibc: the declaration of __sigsetjmp
+// precedes the declaration of sigjmp_buf.
+extern long setjmp(long *); // Can't check, so we trust that this is the right type
+// FIXME: We could still diagnose the missing `jmp_buf` at the point of the call.
+// expected-no-diagnostics
+#elif WRONG_JMP_BUF
typedef long jmp_buf;
-extern int setjmp(char); // expected-warning@8 {{incompatible redeclaration of library function 'setjmp'}}
- // expected-note@8 {{'setjmp' is a builtin with type 'int (jmp_buf)' (aka 'int (long)')}}
+extern int setjmp(char); // expected-warning {{incompatible redeclaration of library function 'setjmp'}}
+ // expected-note@-1 {{'setjmp' is a builtin with type 'int (jmp_buf)' (aka 'int (long)')}}
+#elif RIGHT_JMP_BUF
+typedef long jmp_buf;
+extern int setjmp(long); // OK, right type.
+// expected-no-diagnostics
+#elif ONLY_JMP_BUF
+typedef int *jmp_buf;
#endif
+
+void use() {
+ setjmp(0);
+ #ifdef NO_SETJMP
+ // expected-warning@-2 {{implicit declaration of function 'setjmp' is invalid in C99}}
+ #elif ONLY_JMP_BUF
+ // expected-warning@-4 {{implicitly declaring library function 'setjmp' with type 'int (jmp_buf)' (aka 'int (int *)')}}
+ // expected-note@-5 {{include the header <setjmp.h> or explicitly provide a declaration for 'setjmp'}}
+ #endif
+
+ #ifdef NO_SETJMP
+ // In this case, the regular AST dump doesn't dump the implicit declaration of 'setjmp'.
+ #pragma clang __debug dump setjmp
+ #endif
+}
+
+// CHECK: FunctionDecl {{.*}} used setjmp
+// CHECK: BuiltinAttr {{.*}} Implicit
+// CHECK: ReturnsTwiceAttr {{.*}} Implicit
diff --git a/clang/test/Sema/implicit-builtin-decl.c b/clang/test/Sema/implicit-builtin-decl.c
index b25e86bc03a3..9434b507a3af 100644
--- a/clang/test/Sema/implicit-builtin-decl.c
+++ b/clang/test/Sema/implicit-builtin-decl.c
@@ -54,13 +54,12 @@ main(int argc, char *argv[])
void snprintf() { }
-// PR8316 & PR40692
-void longjmp(); // expected-warning{{declaration of built-in function 'longjmp' requires the declaration of the 'jmp_buf' type, commonly provided in the header <setjmp.h>.}}
+void longjmp();
extern float fmaxf(float, float);
struct __jmp_buf_tag {};
-void sigsetjmp(struct __jmp_buf_tag[1], int); // expected-warning{{declaration of built-in function 'sigsetjmp' requires the declaration of the 'jmp_buf' type, commonly provided in the header <setjmp.h>.}}
+void sigsetjmp(struct __jmp_buf_tag[1], int);
// PR40692
void pthread_create(); // no warning expected