Skip to content

Commit 0312bec

Browse files
zygoloidtstellar
authored andcommitted
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
1 parent fdab756 commit 0312bec

File tree

6 files changed

+116
-32
lines changed

6 files changed

+116
-32
lines changed

clang/include/clang/Basic/Builtins.def

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,9 @@
7575
// U -> pure
7676
// c -> const
7777
// t -> signature is meaningless, use custom typechecking
78+
// T -> type is not important to semantic analysis and codegen; recognize as
79+
// builtin even if type doesn't match signature, and don't warn if we
80+
// can't be sure the type is right
7881
// F -> this is a libc/libm function with a '__builtin_' prefix added.
7982
// f -> this is a libc/libm function without the '__builtin_' prefix. It can
8083
// be followed by ':headername:' to state which header this function
@@ -893,7 +896,7 @@ LANGBUILTIN(__va_start, "vc**.", "nt", ALL_MS_LANGUAGES)
893896
LANGBUILTIN(__fastfail, "vUi", "nr", ALL_MS_LANGUAGES)
894897

895898
// Microsoft library builtins.
896-
LIBBUILTIN(_setjmpex, "iJ", "fj", "setjmpex.h", ALL_MS_LANGUAGES)
899+
LIBBUILTIN(_setjmpex, "iJ", "fjT", "setjmpex.h", ALL_MS_LANGUAGES)
897900

898901
// C99 library functions
899902
// C99 stdarg.h
@@ -987,8 +990,8 @@ LIBBUILTIN(wmemmove,"w*w*wC*z", "f", "wchar.h", ALL_LANGUAGES)
987990
// In some systems setjmp is a macro that expands to _setjmp. We undefine
988991
// it here to avoid having two identical LIBBUILTIN entries.
989992
#undef setjmp
990-
LIBBUILTIN(setjmp, "iJ", "fj", "setjmp.h", ALL_LANGUAGES)
991-
LIBBUILTIN(longjmp, "vJi", "fr", "setjmp.h", ALL_LANGUAGES)
993+
LIBBUILTIN(setjmp, "iJ", "fjT", "setjmp.h", ALL_LANGUAGES)
994+
LIBBUILTIN(longjmp, "vJi", "frT", "setjmp.h", ALL_LANGUAGES)
992995

993996
// Non-C library functions, active in GNU mode only.
994997
// 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)
10151018
LIBBUILTIN(strncasecmp, "icC*cC*z", "f", "strings.h", ALL_GNU_LANGUAGES)
10161019
// POSIX unistd.h
10171020
LIBBUILTIN(_exit, "vi", "fr", "unistd.h", ALL_GNU_LANGUAGES)
1018-
LIBBUILTIN(vfork, "p", "fj", "unistd.h", ALL_LANGUAGES)
1021+
LIBBUILTIN(vfork, "p", "fjT", "unistd.h", ALL_LANGUAGES)
10191022
// POSIX pthread.h
10201023
// FIXME: Should specify argument types.
10211024
LIBBUILTIN(pthread_create, "", "fC<2,3>", "pthread.h", ALL_GNU_LANGUAGES)
10221025

10231026
// POSIX setjmp.h
10241027

1025-
LIBBUILTIN(_setjmp, "iJ", "fj", "setjmp.h", ALL_LANGUAGES)
1026-
LIBBUILTIN(__sigsetjmp, "iSJi", "fj", "setjmp.h", ALL_LANGUAGES)
1027-
LIBBUILTIN(sigsetjmp, "iSJi", "fj", "setjmp.h", ALL_LANGUAGES)
1028-
LIBBUILTIN(savectx, "iJ", "fj", "setjmp.h", ALL_LANGUAGES)
1029-
LIBBUILTIN(getcontext, "iK*", "fj", "setjmp.h", ALL_LANGUAGES)
1028+
LIBBUILTIN(_setjmp, "iJ", "fjT", "setjmp.h", ALL_LANGUAGES)
1029+
LIBBUILTIN(__sigsetjmp, "iSJi", "fjT", "setjmp.h", ALL_LANGUAGES)
1030+
LIBBUILTIN(sigsetjmp, "iSJi", "fjT", "setjmp.h", ALL_LANGUAGES)
1031+
LIBBUILTIN(savectx, "iJ", "fjT", "setjmp.h", ALL_LANGUAGES)
1032+
LIBBUILTIN(getcontext, "iK*", "fjT", "setjmp.h", ALL_LANGUAGES)
10301033

1031-
LIBBUILTIN(_longjmp, "vJi", "fr", "setjmp.h", ALL_GNU_LANGUAGES)
1032-
LIBBUILTIN(siglongjmp, "vSJi", "fr", "setjmp.h", ALL_GNU_LANGUAGES)
1034+
LIBBUILTIN(_longjmp, "vJi", "frT", "setjmp.h", ALL_GNU_LANGUAGES)
1035+
LIBBUILTIN(siglongjmp, "vSJi", "frT", "setjmp.h", ALL_GNU_LANGUAGES)
10331036
// non-standard but very common
10341037
LIBBUILTIN(strlcpy, "zc*cC*z", "f", "string.h", ALL_GNU_LANGUAGES)
10351038
LIBBUILTIN(strlcat, "zc*cC*z", "f", "string.h", ALL_GNU_LANGUAGES)

clang/include/clang/Basic/Builtins.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,13 @@ class Context {
158158
return strchr(getRecord(ID).Attributes, 't') != nullptr;
159159
}
160160

161+
/// Determines whether a declaration of this builtin should be recognized
162+
/// even if the type doesn't match the specified signature.
163+
bool allowTypeMismatch(unsigned ID) const {
164+
return strchr(getRecord(ID).Attributes, 'T') != nullptr ||
165+
hasCustomTypechecking(ID);
166+
}
167+
161168
/// Determines whether this builtin has a result or any arguments which
162169
/// are pointer types.
163170
bool hasPtrArgsOrResult(unsigned ID) const {

clang/lib/Sema/SemaDecl.cpp

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2107,7 +2107,8 @@ NamedDecl *Sema::LazilyCreateBuiltin(IdentifierInfo *II, unsigned ID,
21072107

21082108
// If we have a builtin without an associated type we should not emit a
21092109
// warning when we were not able to find a type for it.
2110-
if (Error == ASTContext::GE_Missing_type)
2110+
if (Error == ASTContext::GE_Missing_type ||
2111+
Context.BuiltinInfo.allowTypeMismatch(ID))
21112112
return nullptr;
21122113

21132114
// 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,
21312132
Context.BuiltinInfo.isHeaderDependentFunction(ID))) {
21322133
Diag(Loc, diag::ext_implicit_lib_function_decl)
21332134
<< Context.BuiltinInfo.getName(ID) << R;
2134-
if (Context.BuiltinInfo.getHeaderName(ID) &&
2135-
!Diags.isIgnored(diag::ext_implicit_lib_function_decl, Loc))
2135+
if (const char *Header = Context.BuiltinInfo.getHeaderName(ID))
21362136
Diag(Loc, diag::note_include_header_or_declare)
2137-
<< Context.BuiltinInfo.getHeaderName(ID)
2138-
<< Context.BuiltinInfo.getName(ID);
2137+
<< Header << Context.BuiltinInfo.getName(ID);
21392138
}
21402139

21412140
if (R.isNull())
@@ -9630,16 +9629,16 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
96309629
}
96319630
}
96329631

9633-
// In C builtins get merged with implicitly lazily created declarations.
9634-
// In C++ we need to check if it's a builtin and add the BuiltinAttr here.
9635-
if (getLangOpts().CPlusPlus) {
9632+
// If this is the first declaration of a library builtin function, add
9633+
// attributes as appropriate.
9634+
if (!D.isRedeclaration() &&
9635+
NewFD->getDeclContext()->getRedeclContext()->isFileContext()) {
96369636
if (IdentifierInfo *II = Previous.getLookupName().getAsIdentifierInfo()) {
96379637
if (unsigned BuiltinID = II->getBuiltinID()) {
96389638
if (NewFD->getLanguageLinkage() == CLanguageLinkage) {
9639-
// Declarations for builtins with custom typechecking by definition
9640-
// don't make sense. Don't attempt typechecking and simply add the
9641-
// attribute.
9642-
if (Context.BuiltinInfo.hasCustomTypechecking(BuiltinID)) {
9639+
// Validate the type matches unless this builtin is specified as
9640+
// matching regardless of its declared type.
9641+
if (Context.BuiltinInfo.allowTypeMismatch(BuiltinID)) {
96439642
NewFD->addAttr(BuiltinAttr::CreateImplicit(Context, BuiltinID));
96449643
} else {
96459644
ASTContext::GetBuiltinTypeError Error;

clang/test/CodeGen/setjmp.c

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// RUN: %clang_cc1 -x c %s -triple x86_64-linux-gnu -emit-llvm -o - | FileCheck %s
2+
// RUN: %clang_cc1 -x c++ %s -triple x86_64-linux-gnu -emit-llvm -o - | FileCheck %s
3+
4+
#ifdef __cplusplus
5+
extern "C" {
6+
#endif
7+
8+
struct __jmp_buf_tag { int n; };
9+
int setjmp(struct __jmp_buf_tag*);
10+
int sigsetjmp(struct __jmp_buf_tag*, int);
11+
int _setjmp(struct __jmp_buf_tag*);
12+
int __sigsetjmp(struct __jmp_buf_tag*, int);
13+
14+
typedef struct __jmp_buf_tag jmp_buf[1];
15+
typedef struct __jmp_buf_tag sigjmp_buf[1];
16+
17+
#ifdef __cplusplus
18+
}
19+
#endif
20+
21+
void f() {
22+
jmp_buf jb;
23+
// CHECK: call {{.*}}@setjmp(
24+
setjmp(jb);
25+
// CHECK: call {{.*}}@sigsetjmp(
26+
sigsetjmp(jb, 0);
27+
// CHECK: call {{.*}}@_setjmp(
28+
_setjmp(jb);
29+
// CHECK: call {{.*}}@__sigsetjmp(
30+
__sigsetjmp(jb, 0);
31+
}
32+
33+
// CHECK: ; Function Attrs: returns_twice
34+
// CHECK-NEXT: declare {{.*}} @setjmp(
35+
36+
// CHECK: ; Function Attrs: returns_twice
37+
// CHECK-NEXT: declare {{.*}} @sigsetjmp(
38+
39+
// CHECK: ; Function Attrs: returns_twice
40+
// CHECK-NEXT: declare {{.*}} @_setjmp(
41+
42+
// CHECK: ; Function Attrs: returns_twice
43+
// CHECK-NEXT: declare {{.*}} @__sigsetjmp(
44+

clang/test/Sema/builtin-setjmp.c

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,42 @@
1-
// RUN: %clang_cc1 -triple x86_64-unknown-unknown -fsyntax-only -verify -DNO_JMP_BUF %s
2-
// RUN: %clang_cc1 -triple x86_64-unknown-unknown -fsyntax-only -verify %s
1+
// RUN: %clang_cc1 -triple x86_64-unknown-unknown -fsyntax-only -verify -DNO_JMP_BUF %s -ast-dump | FileCheck %s
2+
// RUN: %clang_cc1 -triple x86_64-unknown-unknown -fsyntax-only -verify -DWRONG_JMP_BUF %s -ast-dump | FileCheck %s
3+
// RUN: %clang_cc1 -triple x86_64-unknown-unknown -fsyntax-only -verify -DRIGHT_JMP_BUF %s -ast-dump | FileCheck %s
4+
// RUN: %clang_cc1 -triple x86_64-unknown-unknown -fsyntax-only -verify -DONLY_JMP_BUF %s -ast-dump | FileCheck %s
5+
// RUN: %clang_cc1 -triple x86_64-unknown-unknown -fsyntax-only -verify -DNO_SETJMP %s -ast-dump 2>&1 | FileCheck %s
36

47
#ifdef NO_JMP_BUF
5-
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>.}}
6-
#else
8+
// This happens in some versions of glibc: the declaration of __sigsetjmp
9+
// precedes the declaration of sigjmp_buf.
10+
extern long setjmp(long *); // Can't check, so we trust that this is the right type
11+
// FIXME: We could still diagnose the missing `jmp_buf` at the point of the call.
12+
// expected-no-diagnostics
13+
#elif WRONG_JMP_BUF
714
typedef long jmp_buf;
8-
extern int setjmp(char); // expected-warning@8 {{incompatible redeclaration of library function 'setjmp'}}
9-
// expected-note@8 {{'setjmp' is a builtin with type 'int (jmp_buf)' (aka 'int (long)')}}
15+
extern int setjmp(char); // expected-warning {{incompatible redeclaration of library function 'setjmp'}}
16+
// expected-note@-1 {{'setjmp' is a builtin with type 'int (jmp_buf)' (aka 'int (long)')}}
17+
#elif RIGHT_JMP_BUF
18+
typedef long jmp_buf;
19+
extern int setjmp(long); // OK, right type.
20+
// expected-no-diagnostics
21+
#elif ONLY_JMP_BUF
22+
typedef int *jmp_buf;
1023
#endif
24+
25+
void use() {
26+
setjmp(0);
27+
#ifdef NO_SETJMP
28+
// expected-warning@-2 {{implicit declaration of function 'setjmp' is invalid in C99}}
29+
#elif ONLY_JMP_BUF
30+
// expected-warning@-4 {{implicitly declaring library function 'setjmp' with type 'int (jmp_buf)' (aka 'int (int *)')}}
31+
// expected-note@-5 {{include the header <setjmp.h> or explicitly provide a declaration for 'setjmp'}}
32+
#endif
33+
34+
#ifdef NO_SETJMP
35+
// In this case, the regular AST dump doesn't dump the implicit declaration of 'setjmp'.
36+
#pragma clang __debug dump setjmp
37+
#endif
38+
}
39+
40+
// CHECK: FunctionDecl {{.*}} used setjmp
41+
// CHECK: BuiltinAttr {{.*}} Implicit
42+
// CHECK: ReturnsTwiceAttr {{.*}} Implicit

clang/test/Sema/implicit-builtin-decl.c

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,13 +54,12 @@ main(int argc, char *argv[])
5454

5555
void snprintf() { }
5656

57-
// PR8316 & PR40692
58-
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>.}}
57+
void longjmp();
5958

6059
extern float fmaxf(float, float);
6160

6261
struct __jmp_buf_tag {};
63-
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>.}}
62+
void sigsetjmp(struct __jmp_buf_tag[1], int);
6463

6564
// PR40692
6665
void pthread_create(); // no warning expected

0 commit comments

Comments
 (0)