[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Qemu-devel] [PATCH v10 11/13] qapi: Don't box branches of flat unio
From: |
Markus Armbruster |
Subject: |
Re: [Qemu-devel] [PATCH v10 11/13] qapi: Don't box branches of flat unions |
Date: |
Thu, 18 Feb 2016 09:51:00 +0100 |
User-agent: |
Gnus/5.13 (Gnus v5.13) Emacs/24.5 (gnu/linux) |
Eric Blake <address@hidden> writes:
> On 02/17/2016 10:44 AM, Markus Armbruster wrote:
>> Eric Blake <address@hidden> writes:
>>
>>> There's no reason to do two malloc's for a flat union; let's just
>>> inline the branch struct directly into the C union branch of the
>>> flat union.
>>>
>>> Surprisingly, fewer clients were actually using explicit references
>>> to the branch types in comparison to the number of flat unions
>>> thus modified.
>>>
>>> This lets us reduce the hack in qapi-types:gen_variants() added in
>>> the previous patch; we no longer need to distinguish between
>>> alternates and flat unions. It also lets us get rid of all traces
>>> of 'visit_type_implicit_FOO()' in qapi-visit, and reduce one (but
>>> not all) special cases of simplie unions.
>>
>> simple
>>
>>>
>>> Unfortunately, simple unions are not as easy to convert; because
>>> we are special-casing the hidden implicit type with a single 'data'
>>> member, we really DO need to keep calling another layer of
>>> visit_start_struct(), with a second malloc. Hence,
>>> gen_visit_fields_decl() has to special case implicit types (the
>>> type for a simple union variant).
>>
>> Simple unions should be mere sugar for the equivalent flat union, as
>> explained in qapi-code-gen.txt. That they aren't now on the C side is
>> accidental complexity. I hope we can clean that up relatively soon.
>>
>> In the long run, I'd like to replace the whole struct / flat union /
>> simple union mess by a variant record type.
>
> We're getting closer :)
>
>>
>>> Note that after this patch, the only remaining use of
>>> visit_start_implicit_struct() is for alternate types; the next
>>> couple of patches will do further cleanups based on that fact.
>>
>> Remind me, what makes alternates need visit_start_implicit_struct()?
>
> Here's what the two functions do:
>
> visit_start_struct(): optionally allocate, and consume {}
> visit_start_implicit_struct(): allocate, but do not consume {}
>
> When visiting an alternate, we need to allocate the C struct that
> contains the various alternate branches (visit_start_implicit_struct(),
> unchanged by this patch, but renamed visit_start_alternate in 13/13),
> then within that struct, if the next token is '{' we need to visit the C
> struct for the object branch of the alternate (pre-series, that was
> boxed, so we used visit_type_FOO(&obj) which calls visit_start_struct()
> for a full allocation and consumption of {}; but with the previous
> patch, it is now already allocated, so we now use visit_type_FOO(NULL)
> to skip the allocation while still consuming the {}).
>
> I can try to work something along the lines of that text into my commit
> messages for v11.
I've since made it to PATCH 13, and found the fused
visit_start_alternate(). Much easier to comprehend than the
visit_start_implicit_struct() + visit_get_next_type() combo.
>>> Signed-off-by: Eric Blake <address@hidden>
>>>
>>> ---
>>> v10: new patch
>>>
>>> If anything, we could match our simple union wire format more closely
>>> by teaching qapi-types to expose implicit types inline, and write:
>>>
>>> struct SU {
>>> SUKind type;
>>> union {
>>> struct {
>>> Branch1 *data;
>>> } branch1;
>>> struct {
>>> Branch2 *data;
>>> } branch2;
>>> } u;
>>> };
>>>
>>> where we would then access su.u.branch1.data->member instead of
>>> the current su.u.branch1->member.
>>
>> Looks like the cleanup I mentioned above.
>
> Yay, I'm glad you like it! I've already written the patch for it, but it
> was big enough (and needs several other prerequisite cleanups in the
> codebase to use C99 initializers for things like SocketAddress to make
> the switch easier to review) that I haven't posted it yet. And yes, it
> completely gets rid of the simple_union_type() hack.
Looking forward to its demise.
>>> @@ -144,7 +136,7 @@ def gen_variants(variants):
>> for var in variants.variants:
>> # Ugly special case for simple union TODO get rid of it
>> typ = var.simple_union_type() or var.type
>>> ret += mcgen('''
>>> %(c_type)s %(c_name)s;
>>> ''',
>>> - c_type=typ.c_type(is_member=inline),
>>> + c_type=typ.c_type(is_member=not
>>> var.simple_union_type()),
>>> c_name=c_name(var.name))
>>
>> This is where we generate flat union members unboxed: is_member=True
>> suppresses the pointer suffix. Still dislike the name is_member :)
>>
>> Perhaps:
>>
>> # Ugly special case for simple union TODO get rid of it
>> simple_union_type = var.simple_union_type()
>> typ = simple_union_type or var.type
>> ret += mcgen('''
>> %(c_type)s %(c_name)s;
>> ''',
>> c_type=typ.c_type(is_member=not simple_union_type),
>> c_name=c_name(var.name))
>>
>> Slightly more readable, and makes it more clear that "ugly special case"
>> applies to is_member=... as well.
>
> It gets renamed to is_unboxed after the review on 10/13. But even after
> my patch to convert simple unions, this code will still be
> c_type=typ.c_type(is_unboxed=True), unless I figure out a way to rework
> .c_type() to not need two separate boolean flags for the three different
> contexts in which we use a type name (declaring an unboxed member to a
> struct, declaring a local variable, and declaring a const parameter).
A possible alternative to a single c_type() with flags for context would
be separate c_CONTEXT_type().
In QAPISchemaType:
def c_type(self):
pass
def c_param_type(self):
return self.c_type()
QAPISchemaBuiltinType overrides:
def c_type(self):
return self._c_type_name
def c_param_type(self):
if self.name == 'str':
return 'const ' + self._c_type_name
return self._c_type_name
QAPISchemaEnumType:
def c_type(self):
return c_name(self.name)
QAPISchemaArrayType:
def c_type(self):
return c_name(self.name) + pointer_suffix
QAPISchemaObjectType:
def c_type(self):
assert not self.is_implicit()
return c_name(self.name) + pointer_suffix
def c_unboxed_type(self):
return c_name(self.name)
QAPISchemaAlternateType:
def c_type(self):
return c_name(self.name) + pointer_suffix
Lots of trivial code, as so often with OO.
>>> static void visit_type_%(c_type)s_fields(Visitor *v, %(c_type)s *obj,
>>> Error **errp);
>>> ''',
>>> - c_type=typ.c_name())
>>> - struct_fields_seen.add(typ.name)
>>> - return ret
>>
>> Two changes squashed together. First step is mere style:
>
> Then I'll split into two patches for v11.
>
>> Second step is the actual change:
>>
>> @@ -35,7 +39,7 @@ void visit_type_%(c_name)s(Visitor *v, const char *name,
>> %(c_type)sobj, Error **
>>
>>
>> def gen_visit_fields_decl(typ):
>> - if typ.name in struct_fields_seen:
>> + if typ.is_implicit() or typ.name in struct_fields_seen:
>> return ''
>> struct_fields_seen.add(typ.name)
>>
>> Much easier to see what's going on now.
>>
>> I guess you add .is_implicit() here so that gen_visit_object() can call
>> it unconditionally. It's odd; other gen_ functions don't check
>> .is_implicit().
>
> Although I have more plans to use .is_implicit() - I have patches in my
> local tree that allow:
>
> { 'enum': 'Enum', 'data': [ 'branch' ] }
> { 'union': 'U', 'base': { 'anonymous1': 'Enum' },
> 'discriminator': 'anonymous1',
> 'data': { 'branch': { 'anonymous2': 'str' } } }
>
> that is, both an anonymous base, and an anonymous branch. It results in
> more places where we'll need to suppress declarations if the type is
> implicit; so doing it here instead of in each caller proves easier in
> the long run.
>
> But for this patch, I can probably go along with your idea of keeping
> the complexity in the caller, where we highlight that simple unions are
> still special cases for a bit longer...
Yes, please.
>>> @@ -250,9 +221,7 @@ def gen_visit_object(name, base, members, variants):
>>>
>>> if variants:
>>> for var in variants.variants:
>>> - # Ugly special case for simple union TODO get rid of it
>>> - if not var.simple_union_type():
>>> - ret += gen_visit_implicit_struct(var.type)
>>> + ret += gen_visit_fields_decl(var.type)
>>
>> Before: if this is a flat union member of type FOO, we're going to call
>> visit_type_implicit_FOO(), as you can see in the next hunk. Ensure it's
>> in scope by generating it unless it's been generated already.
>>
>> After: we're going to call visit_type_FOO_fields() instead. Generate a
>> forward declaration unless either the function or the forward
>> declaration has been generated already. Except don't generate it when
>> FOO is an implicit type, because then the member is simple rather than
>> flat.
>>
>> Doesn't this unduly hide the ugly special case?
>>
>> To keep it in view, I'd write
>>
>> # Ugly special case for simple union TODO get rid of it
>> if not var.simple_union_type():
>> - ret += gen_visit_implicit_struct(var.type)
>> + ret += gen_visit_fields_decl(var.type)
>>
>> and drop the .is_implicit() from gen_visit_fields_decl().
>>
>> Would this work?
>
> ...It should; I'm testing it now.
>
>>
>> Every time I come across "implicit" structs, I get confused, and have to
>> dig to unconfuse myself. Good to get rid of one.
>
> Yep - and it makes my stalled work on documenting visitor.h easier with
> fewer ugly things to document :)
I welcome a smaller visitor.h; reviewing the first iteration of the
documentation patch was tough going.
>>> case CPU_INFO_ARCH_TRICORE:
>>> - monitor_printf(mon, " PC=0x%016" PRIx64,
>>> cpu->value->u.tricore->PC);
>>> + monitor_printf(mon, " PC=0x%016" PRIx64,
>>> cpu->value->u.tricore.PC);
>>> break;
>>> default:
>>> break;
>>
>> That's not bad at all.
>
> I was actually pleasantly shocked at how few places in code needed
> changing. The conversion of simple unions sitting in my local tree was
> more complex (much of that because we use SocketAddress in a LOT more
> places).
- Re: [Qemu-devel] [PATCH v10 08/13] qapi: Adjust layout of FooList types, (continued)
[Qemu-devel] [PATCH v10 11/13] qapi: Don't box branches of flat unions, Eric Blake, 2016/02/15
[Qemu-devel] [PATCH v10 13/13] qapi: Change visit_start_implicit_struct to visit_start_alternate, Eric Blake, 2016/02/15
[Qemu-devel] [PATCH v10 09/13] qapi: Emit structs used as variants in topological order, Eric Blake, 2016/02/15
[Qemu-devel] [PATCH v10 12/13] qapi: Delete unused visit_start_union(), Eric Blake, 2016/02/15