[PATCH] add support for _Generic

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



It's slightly tested but is fine for the latest kernels
like https://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git locking/kcsan

Note: a known difference with GCC is that it doesn't make the
      distinction between 'signed char' and a plain 'char'
      (on platforms where plain char are signed) since it's using
      the usual type compatbility like used for assignements.

Reference: lore.kernel.org/r/20200527235442.GC1805@xxxxxxx
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@xxxxxxxxx>
---

Given there is some emergency, exceptionally, I've directly
upstreamed this patch but of course comments are most welcome.

-- Luc

 evaluate.c                     |  36 ++++++++
 expand.c                       |   1 +
 expression.c                   |  40 +++++++++
 expression.h                   |  16 ++++
 show-parse.c                   |   3 +
 validation/generic-functions.c |  44 +++++++++
 validation/generic-schar.c     |  39 ++++++++
 validation/generic-typename.c  | 157 +++++++++++++++++++++++++++++++++
 8 files changed, 336 insertions(+)
 create mode 100644 validation/generic-functions.c
 create mode 100644 validation/generic-schar.c
 create mode 100644 validation/generic-typename.c

diff --git a/evaluate.c b/evaluate.c
index 63d75d9031d1..5f2b7d6fc4f1 100644
--- a/evaluate.c
+++ b/evaluate.c
@@ -3272,6 +3272,39 @@ static void check_label_declaration(struct position pos, struct symbol *label)
 	}
 }
 
+static int type_selection(struct symbol *ctrl, struct symbol *type)
+{
+	struct ctype c = { .base_type = ctrl };
+	struct ctype t = { .base_type = type };
+
+	return !type_difference(&c, &t, 0, 0);
+}
+
+struct symbol *evaluate_generic_selection(struct expression *expr)
+{
+	struct type_expression *map;
+	struct expression *res;
+	struct symbol *ctrl;
+
+	if (!(ctrl = evaluate_expression(expr->control)))
+		return NULL;
+
+	for (map = expr->map; map; map = map->next) {
+		if (!evaluate_symbol(map->type))
+			continue;
+		if (!type_selection(ctrl, map->type))
+			continue;
+
+		res = map->expr;
+		goto end;
+	}
+	res = expr->def;
+
+end:
+	*expr = *res;
+	return evaluate_expression(expr);
+}
+
 struct symbol *evaluate_expression(struct expression *expr)
 {
 	if (!expr)
@@ -3357,6 +3390,9 @@ struct symbol *evaluate_expression(struct expression *expr)
 	case EXPR_OFFSETOF:
 		return evaluate_offsetof(expr);
 
+	case EXPR_GENERIC:
+		return evaluate_generic_selection(expr);
+
 	/* These can not exist as stand-alone expressions */
 	case EXPR_INITIALIZER:
 	case EXPR_IDENTIFIER:
diff --git a/expand.c b/expand.c
index ab296c730efd..b07893318382 100644
--- a/expand.c
+++ b/expand.c
@@ -1180,6 +1180,7 @@ static int expand_expression(struct expression *expr)
 	case EXPR_POS:
 		return expand_pos_expression(expr);
 
+	case EXPR_GENERIC:
 	case EXPR_SIZEOF:
 	case EXPR_PTRSIZEOF:
 	case EXPR_ALIGNOF:
diff --git a/expression.c b/expression.c
index 99a6d7568222..1160cd9cc593 100644
--- a/expression.c
+++ b/expression.c
@@ -44,6 +44,8 @@
 #include "target.h"
 #include "char.h"
 
+ALLOCATOR(type_expression, "type-expr-maps");
+
 static int match_oplist(int op, ...)
 {
 	va_list args;
@@ -380,6 +382,40 @@ Enoint:
 	error_die(expr->pos, "constant %s is not a valid number", show_token(token));
 }
 
+static struct token *generic_selection(struct token *token, struct expression **tree)
+{
+	struct expression *expr = alloc_expression(token->pos, EXPR_GENERIC);
+	struct type_expression **last = &expr->map;
+
+	token = expect(token, '(', "after '_Generic'");
+	token = assignment_expression(token, &expr->control);
+	if (!match_op(token, ',')) {
+		goto end;
+	}
+	while (match_op(token, ',')) {
+		token = token->next;
+		if (lookup_type(token)) {
+			struct type_expression *map = __alloc_type_expression(0);
+			token = typename(token, &map->type, NULL);
+			token = expect(token, ':', "after typename");
+			token = assignment_expression(token, &map->expr);
+			*last = map;
+			last = &map->next;
+		} else if (match_ident(token, &default_ident)) {
+			if (expr->def) {
+				warning(token->pos, "multiple default in generic expression");
+				info(expr->def->pos, "note: previous was here");
+			}
+			token = token->next;
+			token = expect(token, ':', "after typename");
+			token = assignment_expression(token, &expr->def);
+		}
+	}
+end:
+	*tree = expr;
+	return expect(token, ')', "after expression");
+}
+
 struct token *primary_expression(struct token *token, struct expression **tree)
 {
 	struct expression *expr = NULL;
@@ -423,6 +459,10 @@ struct token *primary_expression(struct token *token, struct expression **tree)
 				token = builtin_offsetof_expr(token, &expr);
 				break;
 			}
+			if (token->ident == &_Generic_ident) {
+				token = generic_selection(token->next, &expr);
+				break;
+			}
 		} else if (sym->enum_member) {
 			expr = alloc_expression(token->pos, EXPR_VALUE);
 			*expr = *sym->initializer;
diff --git a/expression.h b/expression.h
index 3b79e0f1134e..64aa1fc23309 100644
--- a/expression.h
+++ b/expression.h
@@ -64,6 +64,7 @@ enum expression_type {
 	EXPR_FVALUE,
 	EXPR_SLICE,
 	EXPR_OFFSETOF,
+	EXPR_GENERIC,
 };
 
 
@@ -147,6 +148,14 @@ struct asm_operand {
 	unsigned int is_memory:1;
 };
 
+struct type_expression {
+	struct symbol *type;
+	struct expression *expr;
+	struct type_expression *next;
+};
+
+DECLARE_ALLOCATOR(type_expression);
+
 struct expression {
 	enum expression_type type:8;
 	unsigned flags:8;
@@ -246,6 +255,13 @@ struct expression {
 				struct expression *index;
 			};
 		};
+		// EXPR_GENERIC
+		struct {
+			struct expression *control;
+			struct expression *def;
+			struct type_expression *map;
+			struct expression *result;
+		};
 	};
 };
 
diff --git a/show-parse.c b/show-parse.c
index eb71b6504be4..51a151911e3b 100644
--- a/show-parse.c
+++ b/show-parse.c
@@ -1180,6 +1180,9 @@ int show_expression(struct expression *expr)
 	case EXPR_TYPE:
 		warning(expr->pos, "unable to show type expression");
 		return 0;
+	case EXPR_GENERIC:
+		warning(expr->pos, "unable to show generic expression");
+		return 0;
 	}
 	return 0;
 }
diff --git a/validation/generic-functions.c b/validation/generic-functions.c
new file mode 100644
index 000000000000..61bfd99e2808
--- /dev/null
+++ b/validation/generic-functions.c
@@ -0,0 +1,44 @@
+void funf(float);
+void fund(double);
+void funl(long double);
+
+#define fung(X) _Generic(X,		\
+	float:		funf,		\
+	default:	fund,		\
+	long double:	funl) (X)
+
+#define TEST(name, T)	\
+static void test ## name(T a) { return fung(a); }
+
+TEST(f, float)
+TEST(d, double)
+TEST(l, long double)
+
+/*
+ * check-name: generic-functions
+ * check-command: test-linearize $file
+ *
+ * check-output-start
+testf:
+.L0:
+	<entry-point>
+	call        funf, %arg1
+	ret
+
+
+testd:
+.L2:
+	<entry-point>
+	call        fund, %arg1
+	ret
+
+
+testl:
+.L4:
+	<entry-point>
+	call        funl, %arg1
+	ret
+
+
+ * check-output-end
+ */
diff --git a/validation/generic-schar.c b/validation/generic-schar.c
new file mode 100644
index 000000000000..0b082f4f5edd
--- /dev/null
+++ b/validation/generic-schar.c
@@ -0,0 +1,39 @@
+#define typename(x) _Generic((x) 0,			\
+char:			"char",				\
+signed char:		"signed char",			\
+unsigned char:		"unsigned char",		\
+default:		"???")
+
+#define TEST(name, x)	\
+static const char *test_ ## name(void) { return typename(x); }
+
+TEST(char, char)
+TEST(schar, signed char)
+TEST(uchar, unsigned char)
+
+/*
+ * check-name: generic-schar
+ * check-command: test-linearize --arch=i386 -fsigned-char $file
+ * check-known-to-fail
+ *
+ * check-output-start
+test_char:
+.L0:
+	<entry-point>
+	ret.32      "char"
+
+
+test_schar:
+.L2:
+	<entry-point>
+	ret.32      "signed char"
+
+
+test_uchar:
+.L4:
+	<entry-point>
+	ret.32      "unsigned char"
+
+
+ * check-output-end
+ */
diff --git a/validation/generic-typename.c b/validation/generic-typename.c
new file mode 100644
index 000000000000..1e914c5768b3
--- /dev/null
+++ b/validation/generic-typename.c
@@ -0,0 +1,157 @@
+#define typename(x) _Generic((x) 0,			\
+_Bool:			"_Bool",			\
+char:			"char",				\
+unsigned char:		"unsigned char",		\
+short:			"short",			\
+unsigned short:		"unsigned short",		\
+int:			"int",				\
+unsigned int:		"unsigned int",			\
+long:			"long",				\
+unsigned long:		"unsigned long",		\
+long long:		"long long",			\
+unsigned long long:	"unsigned long long",		\
+float:			"float",			\
+double:			"double",			\
+long double:		"long double",			\
+void *:			"void *",			\
+char *:			"char *",			\
+int *:			"int *",			\
+default:		"???")
+
+#define TEST(name, x)	\
+static const char *test_ ## name(void) { return typename(x); }
+
+TEST(bool, _Bool)
+TEST(char, char)
+TEST(uchar, unsigned char)
+TEST(short, short)
+TEST(ushort, unsigned short)
+TEST(int, int)
+TEST(uint, unsigned int)
+TEST(long, long)
+TEST(ulong, unsigned long)
+TEST(llong, long long)
+TEST(ullong, unsigned long long)
+TEST(float, float)
+TEST(double, double)
+TEST(ldouble, long double)
+TEST(vptr, void *)
+TEST(cptr, char *)
+TEST(iptr, int *)
+TEST(int128, __int128)
+
+/*
+ * check-name: generic-typename
+ * check-command: test-linearize --arch=i386 -fsigned-char $file
+ *
+ * check-output-start
+test_bool:
+.L0:
+	<entry-point>
+	ret.32      "_Bool"
+
+
+test_char:
+.L2:
+	<entry-point>
+	ret.32      "char"
+
+
+test_uchar:
+.L4:
+	<entry-point>
+	ret.32      "unsigned char"
+
+
+test_short:
+.L6:
+	<entry-point>
+	ret.32      "short"
+
+
+test_ushort:
+.L8:
+	<entry-point>
+	ret.32      "unsigned short"
+
+
+test_int:
+.L10:
+	<entry-point>
+	ret.32      "int"
+
+
+test_uint:
+.L12:
+	<entry-point>
+	ret.32      "unsigned int"
+
+
+test_long:
+.L14:
+	<entry-point>
+	ret.32      "long"
+
+
+test_ulong:
+.L16:
+	<entry-point>
+	ret.32      "unsigned long"
+
+
+test_llong:
+.L18:
+	<entry-point>
+	ret.32      "long long"
+
+
+test_ullong:
+.L20:
+	<entry-point>
+	ret.32      "unsigned long long"
+
+
+test_float:
+.L22:
+	<entry-point>
+	ret.32      "float"
+
+
+test_double:
+.L24:
+	<entry-point>
+	ret.32      "double"
+
+
+test_ldouble:
+.L26:
+	<entry-point>
+	ret.32      "long double"
+
+
+test_vptr:
+.L28:
+	<entry-point>
+	ret.32      "void *"
+
+
+test_cptr:
+.L30:
+	<entry-point>
+	ret.32      "char *"
+
+
+test_iptr:
+.L32:
+	<entry-point>
+	ret.32      "int *"
+
+
+test_int128:
+.L34:
+	<entry-point>
+	ret.32      "???"
+
+
+ * check-output-end
+ */
-- 
2.26.2




[Index of Archives]     [Newbies FAQ]     [LKML]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Trinity Fuzzer Tool]

  Powered by Linux