[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Qemu-devel] [PATCH v10 10/13] qapi: Don't box struct branch of alte
From: |
Markus Armbruster |
Subject: |
Re: [Qemu-devel] [PATCH v10 10/13] qapi: Don't box struct branch of alternate |
Date: |
Tue, 16 Feb 2016 20:07:06 +0100 |
User-agent: |
Gnus/5.13 (Gnus v5.13) Emacs/24.5 (gnu/linux) |
Eric Blake <address@hidden> writes:
> There's no reason to do two malloc's for an alternate type visiting
> a QAPI struct; let's just inline the struct directly as the C union
> branch of the struct.
>
> Surprisingly, no clients were actually using the struct member prior
> to this patch; some testsuite coverage is added to avoid future
> regressions.
>
> Ultimately, we want to do the same treatment for QAPI unions, but
> as that touches a lot more client code, it is better as a separate
> patch. So in the meantime, I had to hack in a way to test if we
> are visiting an alternate type, within qapi-types:gen_variants();
> the hack is possible because an earlier patch guaranteed that all
> alternates have at least two branches, with at most one object
> branch; the hack will go away in a later patch.
Suggest:
Ultimately, we want to do the same treatment for QAPI unions, but as
that touches a lot more client code, it is better as a separate patch.
The two share gen_variants(), and I had to hack in a way to test if we
are visiting an alternate type: look for a non-object branch. This
works because alternates have at least two branches, with at most one
object branch, and unions have only object branches. The hack will go
away in a later patch.
> The generated code difference to qapi-types.h is relatively small,
> made possible by a new c_type(is_member) parameter in qapi.py:
Let's drop the "made possible" clause here.
>
> | struct BlockdevRef {
> | QType type;
> | union { /* union tag is @type */
> | void *data;
> |- BlockdevOptions *definition;
> |+ BlockdevOptions definition;
> | char *reference;
> | } u;
> | };
>
> meanwhile, in qapi-visit.h, we create a new visit_type_alternate_Foo(),
> comparable to visit_type_implicit_Foo():
>
> |+static void visit_type_alternate_BlockdevOptions(Visitor *v, const char
> *name, BlockdevOptions *obj, Error **errp)
> |+{
> |+ Error *err = NULL;
> |+
> |+ visit_start_struct(v, name, NULL, 0, &err);
> |+ if (err) {
> |+ goto out;
> |+ }
> |+ visit_type_BlockdevOptions_fields(v, obj, &err);
> |+ error_propagate(errp, err);
> |+ err = NULL;
> |+ visit_end_struct(v, &err);
> |+out:
> |+ error_propagate(errp, err);
> |+}
This is in addition to visit_type_BlockdevOptions(), so we need another
name.
I can't quite see how the function is tied to alternates, though.
>
> and use it like this:
>
> | switch ((*obj)->type) {
> | case QTYPE_QDICT:
> |- visit_type_BlockdevOptions(v, name, &(*obj)->u.definition, &err);
> |+ visit_type_alternate_BlockdevOptions(v, name,
> &(*obj)->u.definition, &err);
Let's compare the two functions. First visit_type_BlockdevOptions():
void visit_type_BlockdevOptions(Visitor *v,
const char *name,
BlockdevOptions **obj,
Error **errp)
{
Error *err = NULL;
visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptions),
&err);
if (err) {
goto out;
}
if (!*obj) {
goto out_obj;
}
visit_type_BlockdevOptions_fields(v, *obj, &err);
if (err) {
goto out_obj;
}
if (!visit_start_union(v, !!(*obj)->u.data, &err) || err) {
goto out_obj;
}
switch ((*obj)->driver) {
case BLOCKDEV_DRIVER_ARCHIPELAGO:
visit_type_implicit_BlockdevOptionsArchipelago(v,
&(*obj)->u.archipelago, &err);
break;
[All the other cases...]
default:
abort();
}
out_obj:
error_propagate(errp, err);
err = NULL;
visit_end_struct(v, &err);
out:
error_propagate(errp, err);
}
Now visit_type_alternate_BlockdevOptions(), with differences annotated
with //:
static void visit_type_alternate_BlockdevOptions(Visitor *v,
const char *name,
BlockdevOptions *obj, // one * less
Error **errp)
{
Error *err = NULL;
visit_start_struct(v, name, NULL, 0, &err); // NULL instead of &obj
// suppresses malloc
if (err) {
goto out;
}
// null check dropped (obj can't be null)
visit_type_BlockdevOptions_fields(v, obj, &err);
// visit_start_union() + switch dropped
error_propagate(errp, err);
err = NULL;
visit_end_struct(v, &err);
out:
error_propagate(errp, err);
}
Why can we drop visit_start_union() + switch?
> | break;
> | case QTYPE_QSTRING:
> | visit_type_str(v, name, &(*obj)->u.reference, &err);
>
> Signed-off-by: Eric Blake <address@hidden>
>
> ---
> v10: new patch
> ---
> scripts/qapi-types.py | 10 ++++++-
> scripts/qapi-visit.py | 49
> +++++++++++++++++++++++++++++----
> scripts/qapi.py | 10 ++++---
> tests/test-qmp-input-visitor.c | 29 ++++++++++++++++++-
> tests/qapi-schema/qapi-schema-test.json | 2 ++
> tests/qapi-schema/qapi-schema-test.out | 2 ++
> 6 files changed, 91 insertions(+), 11 deletions(-)
>
> diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py
> index 2f23432..aba2847 100644
> --- a/scripts/qapi-types.py
> +++ b/scripts/qapi-types.py
> @@ -116,6 +116,14 @@ static inline %(base)s *qapi_%(c_name)s_base(const
> %(c_name)s *obj)
>
>
> def gen_variants(variants):
> + # HACK: Determine if this is an alternate (at least one variant
> + # is not an object); unions have all branches as objects.
> + inline = False
> + for v in variants.variants:
> + if not isinstance(v.type, QAPISchemaObjectType):
> + inline = True
> + break
> +
> # FIXME: What purpose does data serve, besides preventing a union that
> # has a branch named 'data'? We use it in qapi-visit.py to decide
> # whether to bypass the switch statement if visiting the discriminator
> @@ -136,7 +144,7 @@ def gen_variants(variants):
> ret += mcgen('''
> %(c_type)s %(c_name)s;
> ''',
> - c_type=typ.c_type(),
> + c_type=typ.c_type(is_member=inline),
> c_name=c_name(var.name))
>
> ret += mcgen('''
> diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
> index 9ff0337..948bde4 100644
> --- a/scripts/qapi-visit.py
> +++ b/scripts/qapi-visit.py
> @@ -15,10 +15,14 @@
> from qapi import *
> import re
>
> -# visit_type_FOO_implicit() is emitted as needed; track if it has already
> +# visit_type_implicit_FOO() is emitted as needed; track if it has already
> # been output.
> implicit_structs_seen = set()
>
> +# visit_type_alternate_FOO() is emitted as needed; track if it has already
> +# been output.
> +alternate_structs_seen = set()
> +
> # visit_type_FOO_fields() is always emitted; track if a forward declaration
> # or implementation has already been output.
> struct_fields_seen = set()
> @@ -71,6 +75,35 @@ static void visit_type_implicit_%(c_type)s(Visitor *v,
> %(c_type)s **obj, Error *
> return ret
>
>
> +def gen_visit_alternate_struct(typ):
> + if typ in alternate_structs_seen:
> + return ''
> + alternate_structs_seen.add(typ)
> +
> + ret = gen_visit_fields_decl(typ)
> +
> + ret += mcgen('''
> +
> +static void visit_type_alternate_%(c_type)s(Visitor *v, const char *name,
> %(c_type)s *obj, Error **errp)
> +{
> + Error *err = NULL;
> +
> + visit_start_struct(v, name, NULL, 0, &err);
> + if (err) {
> + goto out;
> + }
> + visit_type_%(c_type)s_fields(v, obj, &err);
> + error_propagate(errp, err);
> + err = NULL;
> + visit_end_struct(v, &err);
> +out:
> + error_propagate(errp, err);
> +}
> +''',
> + c_type=typ.c_name())
> + return ret
> +
> +
> def gen_visit_struct_fields(name, base, members):
> ret = ''
>
> @@ -158,11 +191,14 @@ void visit_type_%(c_name)s(Visitor *v, const char
> *name, %(c_name)s *obj, Error
>
> def gen_visit_alternate(name, variants):
> promote_int = 'true'
> + ret = ''
> for var in variants.variants:
> if var.type.alternate_qtype() == 'QTYPE_QINT':
> promote_int = 'false'
> + if isinstance(var.type, QAPISchemaObjectType):
> + ret += gen_visit_alternate_struct(var.type)
>
> - ret = mcgen('''
> + ret += mcgen('''
>
> void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj,
> Error **errp)
> {
> @@ -178,15 +214,18 @@ void visit_type_%(c_name)s(Visitor *v, const char
> *name, %(c_name)s **obj, Error
> }
> switch ((*obj)->type) {
> ''',
> - c_name=c_name(name), promote_int=promote_int)
> + c_name=c_name(name), promote_int=promote_int)
>
> for var in variants.variants:
> + prefix = 'visit_type_'
> + if isinstance(var.type, QAPISchemaObjectType):
> + prefix += 'alternate_'
> ret += mcgen('''
> case %(case)s:
> - visit_type_%(c_type)s(v, name, &(*obj)->u.%(c_name)s, &err);
> + %(prefix)s%(c_type)s(v, name, &(*obj)->u.%(c_name)s, &err);
> break;
> ''',
> - case=var.type.alternate_qtype(),
> + prefix=prefix, case=var.type.alternate_qtype(),
> c_type=var.type.c_name(),
> c_name=c_name(var.name))
>
> diff --git a/scripts/qapi.py b/scripts/qapi.py
> index 4d75d75..dbb58da 100644
> --- a/scripts/qapi.py
> +++ b/scripts/qapi.py
> @@ -821,7 +821,7 @@ class QAPISchemaVisitor(object):
>
>
> class QAPISchemaType(QAPISchemaEntity):
> - def c_type(self, is_param=False):
> + def c_type(self, is_param=False, is_member=False):
> return c_name(self.name) + pointer_suffix
>
> def c_null(self):
> @@ -854,7 +854,7 @@ class QAPISchemaBuiltinType(QAPISchemaType):
> def c_name(self):
> return self.name
>
> - def c_type(self, is_param=False):
> + def c_type(self, is_param=False, is_member=False):
> if is_param and self.name == 'str':
> return 'const ' + self._c_type_name
> return self._c_type_name
> @@ -888,7 +888,7 @@ class QAPISchemaEnumType(QAPISchemaType):
> # See QAPISchema._make_implicit_enum_type()
> return self.name.endswith('Kind')
>
> - def c_type(self, is_param=False):
> + def c_type(self, is_param=False, is_member=False):
> return c_name(self.name)
>
> def member_names(self):
> @@ -984,8 +984,10 @@ class QAPISchemaObjectType(QAPISchemaType):
> assert not self.is_implicit()
> return QAPISchemaType.c_name(self)
>
> - def c_type(self, is_param=False):
> + def c_type(self, is_param=False, is_member=False):
> assert not self.is_implicit()
> + if is_member:
> + return c_name(self.name)
> return QAPISchemaType.c_type(self)
To understand this, you have to peel off OO abstraction:
QAPISchemaType.c_type(self) returns c_name(self.name) + pointer_suffix.
I think we should keep it peeled off in the source code.
>
> def json_type(self):
[tests/ diff looks good]
- [Qemu-devel] [PATCH v10 05/13] qapi-visit: Simplify how we visit common union members, (continued)
- [Qemu-devel] [PATCH v10 05/13] qapi-visit: Simplify how we visit common union members, Eric Blake, 2016/02/15
- [Qemu-devel] [PATCH v10 01/13] qapi: Simplify excess input reporting in input visitors, Eric Blake, 2016/02/15
- [Qemu-devel] [PATCH v10 02/13] qapi: Forbid empty unions and useless alternates, Eric Blake, 2016/02/15
- [Qemu-devel] [PATCH v10 08/13] qapi: Adjust layout of FooList types, Eric Blake, 2016/02/15
- [Qemu-devel] [PATCH v10 10/13] qapi: Don't box struct branch of alternate, Eric Blake, 2016/02/15
- Re: [Qemu-devel] [PATCH v10 10/13] qapi: Don't box struct branch of alternate,
Markus Armbruster <=
[Qemu-devel] [PATCH v10 11/13] qapi: Don't box branches of flat unions, Eric Blake, 2016/02/15