From 3cf7b6dd3b8c464fb7c2107a823d3674019913a0 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Fri, 23 Feb 2018 14:48:57 -0500 Subject: [PATCH] qapi: Adjust layout of FooList types By sticking the next pointer first, we don't need a union with 64-bit padding for smaller types. On 32-bit platforms, this can reduce the size of uint8List from 16 bytes (or 12, depending on whether 64-bit ints can tolerate 4-byte alignment) down to 8. It has no effect on 64-bit platforms (where alignment still dictates a 16-byte struct); but fewer anonymous unions is still a win in my book. It requires visit_next_list() to gain a size parameter, to know what size element to allocate; comparable to the size parameter of visit_start_struct(). I debated about going one step further, to allow for fewer casts, by doing: typedef GenericList GenericList; struct GenericList { GenericList *next; }; struct FooList { GenericList base; Foo *value; }; so that you convert to 'GenericList *' by '&foolist->base', and back by 'container_of(generic, GenericList, base)' (as opposed to the existing '(GenericList *)foolist' and '(FooList *)generic'). But doing that would require hoisting the declaration of GenericList prior to inclusion of qapi-types.h, rather than its current spot in visitor.h; it also makes iteration a bit more verbose through 'foolist->base.next' instead of 'foolist->next'. Note that for lists of objects, the 'value' payload is still hidden behind a boxed pointer. Someday, it would be nice to do: struct FooList { FooList *next; Foo value; }; for one less level of malloc for each list element. This patch is a step in that direction (now that 'next' is no longer at a fixed non-zero offset within the struct, we can store more than just a pointer's-worth of data as the value payload), but the actual conversion would be a task for another series, as it will touch a lot of code. Backports commit e65d89bf1a4484e0db0f3dc820a8b209f2fb1e8b from qemu --- qemu/include/qapi/visitor-impl.h | 2 +- qemu/include/qapi/visitor.h | 10 +++++----- qemu/qapi/qapi-dealloc-visitor.c | 3 ++- qemu/qapi/qapi-visit-core.c | 5 +++-- qemu/qapi/qmp-input-visitor.c | 5 +++-- qemu/qapi/qmp-output-visitor.c | 3 ++- qemu/qapi/string-input-visitor.c | 5 ++--- qemu/scripts/qapi-types.py | 7 ++----- qemu/scripts/qapi-visit.py | 2 +- 9 files changed, 21 insertions(+), 21 deletions(-) diff --git a/qemu/include/qapi/visitor-impl.h b/qemu/include/qapi/visitor-impl.h index be1d1ed6..861a53b1 100644 --- a/qemu/include/qapi/visitor-impl.h +++ b/qemu/include/qapi/visitor-impl.h @@ -38,7 +38,7 @@ struct Visitor void (*end_implicit_struct)(Visitor *v); void (*start_list)(Visitor *v, const char *name, Error **errp); - GenericList *(*next_list)(Visitor *v, GenericList **list); + GenericList *(*next_list)(Visitor *v, GenericList **list, size_t size); void (*end_list)(Visitor *v); /* May be NULL; only needed for input visitors. */ diff --git a/qemu/include/qapi/visitor.h b/qemu/include/qapi/visitor.h index 1cf4634d..023fc3e7 100644 --- a/qemu/include/qapi/visitor.h +++ b/qemu/include/qapi/visitor.h @@ -18,13 +18,13 @@ #include "qapi/error.h" #include +/* This struct is layout-compatible with all other *List structs + * created by the qapi generator. It is used as a typical + * singly-linked list. */ typedef struct GenericList { - union { - void *value; - uint64_t padding; - }; struct GenericList *next; + char padding[]; } GenericList; void visit_start_struct(Visitor *v, const char *name, void **obj, @@ -34,7 +34,7 @@ void visit_start_implicit_struct(Visitor *v, void **obj, size_t size, Error **errp); void visit_end_implicit_struct(Visitor *v); void visit_start_list(Visitor *v, const char *name, Error **errp); -GenericList *visit_next_list(Visitor *v, GenericList **list); +GenericList *visit_next_list(Visitor *v, GenericList **list, size_t size); void visit_end_list(Visitor *v); /** diff --git a/qemu/qapi/qapi-dealloc-visitor.c b/qemu/qapi/qapi-dealloc-visitor.c index e1bb5718..451fdcdc 100644 --- a/qemu/qapi/qapi-dealloc-visitor.c +++ b/qemu/qapi/qapi-dealloc-visitor.c @@ -101,7 +101,8 @@ static void qapi_dealloc_start_list(Visitor *v, const char *name, Error **errp) qapi_dealloc_push(qov, NULL); } -static GenericList *qapi_dealloc_next_list(Visitor *v, GenericList **listp) +static GenericList *qapi_dealloc_next_list(Visitor *v, GenericList **listp, + size_t size) { GenericList *list = *listp; QapiDeallocVisitor *qov = to_qov(v); diff --git a/qemu/qapi/qapi-visit-core.c b/qemu/qapi/qapi-visit-core.c index 59b01bb1..e2545bde 100644 --- a/qemu/qapi/qapi-visit-core.c +++ b/qemu/qapi/qapi-visit-core.c @@ -52,9 +52,10 @@ void visit_start_list(Visitor *v, const char *name, Error **errp) v->start_list(v, name, errp); } -GenericList *visit_next_list(Visitor *v, GenericList **list) +GenericList *visit_next_list(Visitor *v, GenericList **list, size_t size) { - return v->next_list(v, list); + assert(list && size >= sizeof(GenericList)); + return v->next_list(v, list, size); } void visit_end_list(Visitor *v) diff --git a/qemu/qapi/qmp-input-visitor.c b/qemu/qapi/qmp-input-visitor.c index 3fb9f0e9..b2c166a1 100644 --- a/qemu/qapi/qmp-input-visitor.c +++ b/qemu/qapi/qmp-input-visitor.c @@ -173,7 +173,8 @@ static void qmp_input_start_list(Visitor *v, const char *name, Error **errp) qmp_input_push(qiv, qobj, errp); } -static GenericList *qmp_input_next_list(Visitor *v, GenericList **list) +static GenericList *qmp_input_next_list(Visitor *v, GenericList **list, + size_t size) { QmpInputVisitor *qiv = to_qiv(v); GenericList *entry; @@ -192,7 +193,7 @@ static GenericList *qmp_input_next_list(Visitor *v, GenericList **list) return NULL; } - entry = g_malloc0(sizeof(*entry)); + entry = g_malloc0(size); if (first) { *list = entry; } else { diff --git a/qemu/qapi/qmp-output-visitor.c b/qemu/qapi/qmp-output-visitor.c index af8d1866..dfaa6caa 100644 --- a/qemu/qapi/qmp-output-visitor.c +++ b/qemu/qapi/qmp-output-visitor.c @@ -127,7 +127,8 @@ static void qmp_output_start_list(Visitor *v, const char *name, Error **errp) qmp_output_push(qov, list); } -static GenericList *qmp_output_next_list(Visitor *v, GenericList **listp) +static GenericList *qmp_output_next_list(Visitor *v, GenericList **listp, + size_t size) { GenericList *list = *listp; QmpOutputVisitor *qov = to_qov(v); diff --git a/qemu/qapi/string-input-visitor.c b/qemu/qapi/string-input-visitor.c index d695f072..ceceef52 100644 --- a/qemu/qapi/string-input-visitor.c +++ b/qemu/qapi/string-input-visitor.c @@ -140,8 +140,7 @@ start_list(Visitor *v, const char *name, Error **errp) } } -static GenericList * -next_list(Visitor *v, GenericList **list) +static GenericList *next_list(Visitor *v, GenericList **list, size_t size) { StringInputVisitor *siv = to_siv(v); GenericList **link; @@ -175,7 +174,7 @@ next_list(Visitor *v, GenericList **list) link = &(*list)->next; } - *link = g_malloc0(sizeof **link); + *link = g_malloc0(size); return *link; } diff --git a/qemu/scripts/qapi-types.py b/qemu/scripts/qapi-types.py index 86e71f25..7d9fb481 100644 --- a/qemu/scripts/qapi-types.py +++ b/qemu/scripts/qapi-types.py @@ -28,11 +28,8 @@ typedef struct %(c_name)s %(c_name)s; def gen_array(name, element_type): return mcgen(''' struct %(c_name)s { - union { - %(c_type)s value; - uint64_t padding; - }; - struct %(c_name)s *next; + %(c_name)s *next; + %(c_type)s value; }; ''', c_name=c_name(name), c_type=element_type.c_type()) diff --git a/qemu/scripts/qapi-visit.py b/qemu/scripts/qapi-visit.py index cf8680ff..0d486171 100644 --- a/qemu/scripts/qapi-visit.py +++ b/qemu/scripts/qapi-visit.py @@ -123,7 +123,7 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error } for (prev = (GenericList **)obj; - !err && (i = visit_next_list(v, prev)) != NULL; + !err && (i = visit_next_list(v, prev, sizeof(**obj))) != NULL; prev = &i) { %(c_name)s *native_i = (%(c_name)s *)i; visit_type_%(c_elt_type)s(v, NULL, &native_i->value, &err);