Sparse has support for a subset of GCC's large collection of builtin functions. As for GCC, it's not easy to know which builtins are supported in which versions. clang has a good solution to this problem: it adds the checking macro __has_builtin(<name>) which evaluates to 1 if <name> is a builtin function supported by the compiler and 0 otherwise. It can be used like: #if __has_builtin(__builtin_clz) #define clz(x) __builtin_clz(x) #else ... #endif It's possible or probable that GCC will have this soon too: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66970 Add support for this __has_builtin() macro by extending the evaluation of preprocessor expressions very much like it is done to support defined(). Note: Some function-like builtin features, like __builtin_offset(), are considered as a kind of keyword/operator and processed as such. These are *not* considered as builtins by __has_builtin(). Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@xxxxxxxxx> --- builtin.c | 2 ++ ident-list.h | 1 + lib.c | 1 + pre-process.c | 37 +++++++++++++++++++++++ symbol.h | 1 + validation/preprocessor/has-builtin.c | 43 +++++++++++++++++++++++++++ 6 files changed, 85 insertions(+) create mode 100644 validation/preprocessor/has-builtin.c diff --git a/builtin.c b/builtin.c index 0114c4ca9..b3460847b 100644 --- a/builtin.c +++ b/builtin.c @@ -360,6 +360,7 @@ void init_builtins(int stream) sym->ctype.base_type = ptr->base_type; sym->ctype.modifiers = ptr->modifiers; sym->op = ptr->op; + sym->builtin = 1; } } @@ -373,6 +374,7 @@ static void declare_builtin(const char *name, struct symbol *rtype, int variadic sym->ctype.base_type = fun; sym->ctype.modifiers = MOD_TOPLEVEL; + sym->builtin = 1; fun->ctype.base_type = rtype; fun->variadic = variadic; diff --git a/ident-list.h b/ident-list.h index 2f1fecb48..9abb2e5a7 100644 --- a/ident-list.h +++ b/ident-list.h @@ -59,6 +59,7 @@ IDENT_RESERVED(__label__); * sparse. */ IDENT(defined); IDENT(once); +IDENT(__has_builtin); __IDENT(pragma_ident, "__pragma__", 0); __IDENT(__VA_ARGS___ident, "__VA_ARGS__", 0); __IDENT(__LINE___ident, "__LINE__", 0); diff --git a/lib.c b/lib.c index c451a88ce..b33e5b946 100644 --- a/lib.c +++ b/lib.c @@ -1244,6 +1244,7 @@ static void create_builtin_stream(void) assert (0); } + add_pre_buffer("#define __has_builtin(x) 0\n"); add_pre_buffer("#define __builtin_stdarg_start(a,b) ((a) = (__builtin_va_list)(&(b)))\n"); add_pre_buffer("#define __builtin_va_start(a,b) ((a) = (__builtin_va_list)(&(b)))\n"); add_pre_buffer("#define __builtin_ms_va_start(a,b) ((a) = (__builtin_ms_va_list)(&(b)))\n"); diff --git a/pre-process.c b/pre-process.c index 7bc4a00fd..783ce6987 100644 --- a/pre-process.c +++ b/pre-process.c @@ -158,6 +158,12 @@ static void replace_with_defined(struct token *token) replace_with_bool(token, token_defined(token)); } +static void replace_with_has_builtin(struct token *token) +{ + struct symbol *sym = lookup_symbol(token->ident, NS_SYMBOL); + replace_with_bool(token, sym && sym->builtin); +} + static int expand_one_symbol(struct token **list) { struct token *token = *list; @@ -1509,6 +1515,10 @@ static int expression_value(struct token **where) state = 1; beginning = list; break; + } else if (p->ident == &__has_builtin_ident) { + state = 4; + beginning = list; + break; } if (!expand_one_symbol(list)) continue; @@ -1539,6 +1549,33 @@ static int expression_value(struct token **where) sparse_error(p->pos, "missing ')' after \"defined\""); *list = p->next; continue; + + // __has_builtin(xyz) + case 4: + if (match_op(p, '(')) { + state = 5; + } else { + sparse_error(p->pos, "missing '(' after \"__has_builtin\""); + state = 0; + } + *beginning = p; + break; + case 5: + if (token_type(p) != TOKEN_IDENT) { + sparse_error(p->pos, "identifier expected"); + state = 0; + break; + } + if (!match_op(p->next, ')')) + sparse_error(p->pos, "missing ')' after \"__has_builtin\""); + state = 6; + replace_with_has_builtin(p); + *beginning = p; + break; + case 6: + state = 0; + *list = p->next; + continue; } list = &p->next; } diff --git a/symbol.h b/symbol.h index 2792df24f..2527584e2 100644 --- a/symbol.h +++ b/symbol.h @@ -173,6 +173,7 @@ struct symbol { designated_init:1, forced_arg:1, accessed:1, + builtin:1, transparent_union:1; struct expression *array_size; struct ctype ctype; diff --git a/validation/preprocessor/has-builtin.c b/validation/preprocessor/has-builtin.c new file mode 100644 index 000000000..03272fc95 --- /dev/null +++ b/validation/preprocessor/has-builtin.c @@ -0,0 +1,43 @@ +#ifndef __has_builtin +__has_builtin()??? Quesako? +#define __has_builtin(x) 0 +#else +"has __has_builtin(), yeah!" +#endif + +#if __has_builtin(nothing) +#error "not a builtin!" +#endif + +#if __has_builtin(__builtin_offsetof) \ + || __has_builtin(__builtin_types_compatible_p) +#error "builtin ops are not builtin functions!" +#endif + +#if __has_builtin(__builtin_va_list) \ + || __has_builtin(__builtin_ms_va_list) +#error "builtin types are not builtin functions!" +#endif + +#if __has_builtin(__builtin_abs) +abs +#endif + +#if __has_builtin(__builtin_constant_p) +constant_p +#endif + +123 __has_builtin(abc) def + +/* + * check-name: has-builtin + * check-command: sparse -E $file + * + * check-output-start + +"has __has_builtin(), yeah!" +abs +constant_p +123 0 def + * check-output-end + */ -- 2.17.1 -- To unsubscribe from this list: send the line "unsubscribe linux-sparse" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html