Skip to content
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

[sil-mem2reg] Add a simple scope data structure and use it to simplify some code. #36913

Merged

Conversation

gottesmm
Copy link
Member

We have for a long time talked about creating a scope like data structure for
use in the SILOptimizer. The discussion was whether or not to reuse the
infrastructure in SILGen that does this already. There were concerns about doing
so since the code in the SILOptimizer and SILGen can work differently.

With that in mind, I added a small AssertingScope class and built on top of that
a composition SIL level class called SILOptScope that one can use to add various
cleanups. One is able to both destructively pop at end of scope and pop along
early exits.

At an implementation level, I kept it simple and:

  1. Represented a scope as a stack of Optional which are just a wrapper
    around a std::function. The Optional is so that we can invalidate a cleanup.
  2. Based all of these scopes around the idea that the user of the scope must
    invalidate the scope by hand. If not, the scope object will assert at the end
    of its RAII scope.
  3. Rather than creating a whole class hierarchy, I just used std::function
    closures to keep things simple.

…y some code.

We have for a long time talked about creating a scope like data structure for
use in the SILOptimizer. The discussion was whether or not to reuse the
infrastructure in SILGen that does this already. There were concerns about doing
so since the code in the SILOptimizer and SILGen can work differently.

With that in mind, I added a small AssertingScope class and built on top of that
a composition SIL level class called SILOptScope that one can use to add various
cleanups. One is able to both destructively pop at end of scope and pop along
early exits.

At an implementation level, I kept it simple and:

1. Represented a scope as a stack of Optional<Cleanup> which are just a wrapper
   around a std::function. The Optional is so that we can invalidate a cleanup.
2. Based all of these scopes around the idea that the user of the scope must
   invalidate the scope by hand. If not, the scope object will assert at the end
   of its RAII scope.
3. Rather than creating a whole class hierarchy, I just used std::function
   closures to keep things simple.
@gottesmm
Copy link
Member Author

@swift-ci test

@swift-ci
Copy link
Collaborator

Build failed
Swift Test OS X Platform
Git Sha - c0e31d8

@gottesmm
Copy link
Member Author

@swift-ci test macOS platform

Copy link
Member

@atrick atrick left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

The code looks good. I didn't try to compare this with any other examples of similar utilities, but I can't immediately think of a better way.

A few comments for future work.

/// on Optional has value.
void invalidateCleanup(unsigned cleanupIndex) {
assert(!didPop && "Should not invalidate once popped?!");
assert(cleanupIndex < cleanups.size());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I don't think you need to explicitly bounds-check a data structure that's already bounds checked.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll fix this in a follow on commit.

}

/// Can not be called once the scope is moved.
void nonDestructivePop(Args... args) const {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For future consideration. It seems like the point of nonDestructivePops would be to copy the scope without making a physical copy. So it would be more robust/usable to allow scopes to be either copied or chained with multiple scopes sharing the cleanup vector. Then popping is only destructive when you're the original or unique owner of those cleanups. I don't know if copying (with CoW behavior) or chaining with shared cleanups would make more sense in practice.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would need to think about this. The point of this is to enable one to along early exiting paths pop all current scoped things.

In SILGen, the scope data structure allows for a shared cleanup vector. I think that would be a great add-on. I was trying to keep this simple.

return pushDestroyValue(copy);
}

ScopedValue pushEndBorrow(SILValue value) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's slightly questionable as to why you'd bother creating a ScopedValue when there's no cancelable cleanup, but seems fine. Presumably you could assert that the scope is still active when the ScopedValue is used, but I don't know if there's any intention to actually do that.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the reason why I did this is that in SILGen we found it interesting to be able to write APIs against ManagedValue that both had a cleanup/did not have a cleanup without having to change the currency type and thus have multiple APIs.

@gottesmm gottesmm merged commit 8c9d402 into apple:main Apr 15, 2021
@gottesmm gottesmm deleted the pr-45e4affc7e9132e241abc77bc7eef73664feef8a branch April 15, 2021 02:54
gottesmm added a commit to gottesmm/swift that referenced this pull request Apr 15, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants