aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
authorLuc Van Oostenryck <luc.vanoostenryck@gmail.com>2018-06-08 03:38:29 +0200
committerLuc Van Oostenryck <luc.vanoostenryck@gmail.com>2018-06-08 18:55:02 +0200
commita6e80f36afca7b7f6eeaf557cdbd1effca5cc810 (patch)
treee272207624986109e767bd2790c921adcf240f16
parent35b50cdb51b6c32262103672277135cb0b5311e6 (diff)
downloadsparse-dev-a6e80f36afca7b7f6eeaf557cdbd1effca5cc810.tar.gz
builtin: add support for __has_builtin()
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@gmail.com>
-rw-r--r--builtin.c2
-rw-r--r--ident-list.h1
-rw-r--r--lib.c1
-rw-r--r--pre-process.c37
-rw-r--r--symbol.h1
-rw-r--r--validation/preprocessor/has-builtin.c43
6 files changed, 85 insertions, 0 deletions
diff --git a/builtin.c b/builtin.c
index 0114c4ca..b3460847 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 5394f587..a37a4a1b 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(__func___ident, "__func__", 0);
diff --git a/lib.c b/lib.c
index 30d1cf6d..881a4451 100644
--- a/lib.c
+++ b/lib.c
@@ -1246,6 +1246,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 b44231fd..547ce092 100644
--- a/pre-process.c
+++ b/pre-process.c
@@ -159,6 +159,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 void expand_line(struct token *token)
{
replace_with_integer(token, token->pos.line);
@@ -1541,6 +1547,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;
@@ -1571,6 +1581,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 eed4edd2..6d23503f 100644
--- a/symbol.h
+++ b/symbol.h
@@ -174,6 +174,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 00000000..03272fc9
--- /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
+ */