-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[sil] [irgen] make object instruction valid #30736
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@zoecarver I'm not sure if this makes sense. I would not say that alloc_refs behaves differently than other instructions. What you call 'to pretend to be a pointer' is a feature of the swift/SIL type system: that's what we call a 'reference type'. It's true that the implementation of a reference is done with a pointer, but that's just how it is implemented. In SIL, a reference type has a very well defined semantic and it can be copied ( = increase ref count), destroyed (= decrease ref count), etc. If you want to represent the contents of a class as a value-type in SIL, then this would need a change in the type system rather than just using a different allocation instruction. In your proposal, the result of 'object' is still a reference type (a ref_element_addr of an object - what does this even mean?) A straw man proposal: we could introduce a new SIL type, kind of a wrapper-type,
we can just use a
It also has the advantage that all SIL optimization would work with this out of the box. |
@eeckstein I completely agree with your points.
The problem I am trying to articulate is this (and I probably didn't do a great job articulating it): if we generalize some of the current passes to support alloc_ref instructions, it is likely that we will, for example, get a My original solution to this problem was to emit an Anyway, I love your proposal! It solves a lot of problems with this patch (doesn't require stack allocation, fixes the above problem, doesn't require any optimizer changes, etc.). What do you think the next steps should be? Do any wrapper types like that exist? Or would this be a new kind of thing? #30743 could probably be updated with minimal changes to create a |
Thinking about it again: why not just use a tuple which contains all the class properties? |
I suppose that would probably work. It would also remove the wrapper which would be nice. I think the best way to do this will be to create an alloc_stack and replace all stores and ref_element_addrs with copy_addrs and tuple_element_addrs respectively. Then we can just ref cast and copy_addr the tuple to the alloc_ref. I really appreciate your suggestions! This will now be a much better implementation. Let me know if you have any other ideas. |
Adding a new type in SIL optimizations might be a problem, because AFAIK, the AST is considered immutable after the frontend. @slavapestov or @DougGregor can comment on this. An alternative would be to add this kind of "wrapper-type" just in SIL: add an API layer for struct types in SILType, which either redirect to the AST struct (in most cases) or map the class properties for the wrapper-type case. |
I think your tuple idea will work well. I've been playing around with it and looking at the IR generated and it seems to have all the benefits of this patch without the drawbacks. In order to efficiently copy the tuple pointer to the class ref I've added a new sil instruction. We don't necessarily need another sil instruction but it allows us to copy the whole object with a memcpy which I'm not sure we could otherwise do. I'll put it up for review later today and we can decide whether adding another instruction is worth the cost. |
This patch allows the object instruction to be used everywhere.
Why the change?
Why is this change beneficial? It may appear that it generates the same code as an alloc_ref on the stack. That is mostly true.
The problem with alloc_ref is that it behaves very differently from most other sil instructions. It pretends to be a value but it is actually a pointer. It makes it hard for both the sil optimizer and the llvm optimizer to reason about what's going on. This change allows objects (classes) to be created in a way that is much more semantically similar to structs, enums, and tuples which will allow our current sil otpimization passes to optimize class objects with little to no changes.
For example, mem2reg cannot optimize class objects at all. Adding support would mean re-writing almost the entire pass. However, if objects are created using the object instruction, it can be updated to support objects with only two lines changed in general utility files. That's great.
Additionally, it seems to make it easier for llvm to reason about what's going on as well. In another patch I added support for promoting
alloc_ref
s toobject
s. And, after making no other change to the sil optimizer (i.e. the sil optimizer still didn't know that objects existed) the llvm optimizer was able to reason about what was going on much more easily.For example
Before this patch:
After this patch:
Why stack?
I spent a few days trying to get this to work when
visitObjectInst
just emitted anExplosion
. In the future, I think this is a thing we can (and should) do but, right now it would mean updating almost every use of a class to handle two paths: analloc_ref
object and anobject
object. This is becausealloc_ref
pretends to be a value when it's actually a pointer. The simplest solution was to makeobject
essentially a stack version ofalloc_ref
and have it pretend to be a pointer as well.Future plans
I have two more patches that I'll put up for review today. One updates
StackPromotion
to promote stackalloc_ref
s toobject
s and the other adds some basic support forobject
instructions across the sil optimizer.Sorry for all the words here :P TL;DR: this patch lays the groundwork for extending struct optimizations to classes as well.