Skip to content

Add a phase that removes ResolvedTypeRefs before codegen #534

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Open
fitzgen opened this issue Feb 21, 2017 · 23 comments
Open

Add a phase that removes ResolvedTypeRefs before codegen #534

fitzgen opened this issue Feb 21, 2017 · 23 comments

Comments

@fitzgen
Copy link
Member

fitzgen commented Feb 21, 2017

Ever since we got the graphviz visualizations of our IR graph, it has become immediately clear that we have a TON of ResolvedTypeRefs all over the place. In fact, 8 out of 15 IR items (more than half!) are ResolvedTypeRefs in this example I happen to be looking at:

using

A pass over the IR items where we replace all references to ResolvedTypeRefs with references to the type being transitively referenced via the ResolvedTypeRef would probably give us speed ups in codegen and analysis, because we wouldn't have to look at useless items anymore. It would also make looking at these graphs easier, since there would be less visual noise :-P

A good time to do this would be in ir::context::BindgenContext::gen, after we resolve unresolved type refs in resolve_typerefs. https://github.com/servo/rust-bindgen/blob/master/src/ir/context.rs#L529

I can mentor whoever would like to take a stab at this.

@fitzgen
Copy link
Member Author

fitzgen commented Feb 21, 2017

If you want to pick this up, leave a comment and I can assign you :)

@samliu
Copy link

samliu commented Feb 21, 2017

Hey Nick! (How are you?!) I've been learning Rust and this looks really interesting. I'd be down to take a stab :)

@fitzgen
Copy link
Member Author

fitzgen commented Feb 21, 2017

Great! Let me know if you have any questions or anything like that.

I just realized that fully removing ResolvedTypeRefs would require modifying ir::traversal::Trace to be generic over either &mut ItemId edges or just ItemIds like it is now. This is a bit more tricky than I anticipated, and so I think we should resign ourselves to flattening ResolvedTypeRef chains to a single ResolvedTypeRef. That is,

ResolvedTypeRef -> ResolvedTypeRef -> ResolvedTypeRef -> SomeType

should become:

ResolvedTypeRef -> SomeType

The core of this new phase should look something like this pseudo code:

impl BindgenContext {
    // ...

    fn flatten_resolved_type_ref(&mut self) {
        for (id, item) in self.items {
            if item is a resolved type ref {
                self.flatten_one_resolved_type_ref(id);
            }
        }
    }

    // Recursively walk the chain of ResolvedTypeRefs and flatten it as we
    // come back out of the recursion.
    fn flatten_one_resolved_type_ref(&mut self, id: ItemId) -> ItemId { 
        if id's item is a resolved type ref {
            let final_referenced_id = self.flatten_one_resolved_type_ref(item's referenced type);
            item's referenced type = final_referenced_id;
            final_referenced_id
        } else {
            id
        }
    }

    // ...
}

Bonus points for keeping a debug-only set of items we've walked in the recursive function and asserting that we don't get a cycle :) See https://github.com/servo/rust-bindgen/blob/master/src/ir/item.rs#L69-L87

@fitzgen
Copy link
Member Author

fitzgen commented Feb 21, 2017

(Also, woah! Hey! Didn't read your username very carefully, at first! :-P)

@emilio
Copy link
Contributor

emilio commented Feb 22, 2017

I have an issue with this, and is that this would lose the constness information, which we do use for code generation.

@fitzgen
Copy link
Member Author

fitzgen commented Feb 22, 2017

I have an issue with this, and is that this would lose the constness information, which we do use for code generation.

Crap, I forgot about that. It would be great if we separated the concept of a resolved type reference from a const <type>, then we could move forward here.

@samliu interested in this prerequisite groundwork?

@samliu
Copy link

samliu commented Feb 22, 2017 via email

@emilio
Copy link
Contributor

emilio commented Feb 23, 2017

As an idea, we probably want to call this something like QualifiedType,
and it'd be something like:

struct Qualifiers {
    is_const: bool,
}

struct QualifiedType {
    ty: ItemId,
    qualifiers: Qualifiers,
}

@fitzgen
Copy link
Member Author

fitzgen commented Feb 23, 2017

Yep, exactly what I was imagining!

It will also need to be Traced from the impl Trace for Type as well (either directly there, or as a second Trace implementation that is called from there).

@fitzgen
Copy link
Member Author

fitzgen commented Feb 23, 2017

(And there would be a new TypeKind variant called TypeKind::Qualified(QualifiedType) or something)

@emilio
Copy link
Contributor

emilio commented Feb 23, 2017

(And there would be a new TypeKind variant called TypeKind::Qualified(QualifiedType) or something)

Why would that be needed? We only care about qualifiers in top-level types, right?

I.e., const StructName* foo; would be a TypeKind::Pointer(QualifiedType), and so on with fields, arrays, etc.

Am I missing something?

@emilio
Copy link
Contributor

emilio commented Feb 23, 2017

Why would that be needed? We only care about qualifiers in top-level types, right?

With top-level types, I mean in the stuff that holds from TypeKind, Var, etc.

@fitzgen
Copy link
Member Author

fitzgen commented Feb 23, 2017

I don't understand the distinction you are trying to make, sorry if I'm being thick...

@fitzgen
Copy link
Member Author

fitzgen commented Feb 23, 2017

I.e., const StructName* foo; would be a TypeKind::Pointer(QualifiedType), and so on with fields, arrays, etc.

Aha, I see where our imaginations are diverging.

I would imagine const Structname* to result in IR like

ItemId(0) = Item(TypeKind::Pointer(ItemId(1)))
ItemId(1) = Item(TypeKind::Qualified(ItemId(2)))
ItemId(2) = Item(TypeKind::Comp(StructName))

@emilio
Copy link
Contributor

emilio commented Feb 23, 2017

Right, I was imagining making QualifiedType a thin wrapper on top of an ItemId. I think it's worth for a few reasons:

  • First, you don't need all the memory impact of an Item to just indicate that a type is const.
  • Second, I think the qualifications are orthogonal to the type, so I think having a type being a qualified type is confusing. Also, we'd need to guarantee that a TypeKind::Qualified wouldn't point to another of the same kind, etc.
  • Third, the approach you're suggesting makes us having to create two items in common paths that now create one (like pointer construction), which would be a bigger refactor than needed IMO.
  • Finally, clang seems to agree with me in this regard, and it uses internally a structure similar to this (see the QualType definition).

WDYT? I think representing a struct field like:

struct foo {
    const struct bar* const bar_field;
};

As:

QualifiedType(ItemId(1), const)

where:

ItemId(1): TypeKind::Pointer(QualifiedType(ItemId(2), const))
ItemId(2): TypeKind::Comp(..)

Is more appealing than using the already arguably somewhat bloated Type.

@fitzgen
Copy link
Member Author

fitzgen commented Feb 23, 2017

Ok, yeah that sounds well-motivated.

@fitzgen
Copy link
Member Author

fitzgen commented Mar 6, 2017

@samliu any progress? any questions I can help answer? :)

@samliu
Copy link

samliu commented Mar 6, 2017 via email

@fitzgen
Copy link
Member Author

fitzgen commented Mar 6, 2017

No worries!

There's no rush with this, so as long as you still plan on tackling it, we can keep it assigned. If for whatever reason you don't want to or can't work on this anymore, then we can unassign and let someone else grab it.

@samliu
Copy link

samliu commented Mar 6, 2017 via email

@RReverser
Copy link

RReverser commented Jul 27, 2017

Was just about to report this (more precisely, #511), but looks like a known issue. Is there anything I could help with?

@fitzgen
Copy link
Member Author

fitzgen commented Jul 27, 2017

@RReverser if you wanted to pick this up, I think that's fine, given that there's been silence for a while.

@samliu you're not still planning on doing this, are you?

@samliu
Copy link

samliu commented Jul 27, 2017 via email

# for free to join this conversation on GitHub. Already have an account? # to comment
Projects
None yet
Development

No branches or pull requests

4 participants