-
-
Notifications
You must be signed in to change notification settings - Fork 400
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
Implement scope analysis and local variables #3988
Conversation
Test262 conformance changes
|
Impressive work! I'm really liking that we're starting to add some static analysis to the processed code, instead of only relying on runtime optimizations. About the method of storing the scopes, is adding the scope info directly into the AST the best approach? Because I was thinking that in the future we'd have an architecture similar to rustc's; every node has a |
That would be pretty similar to a flattened AST just for the metadata, right? I think that should work for the most part. That could also remove the need for the interior mutability in
Just for clarification; the scope analysis is also done after parsing with this approach. You could just not do it if it's not interesting for your AST use case.
I definitley would like to give it a try. It could also be a start for a full flat AST. I've not read about it in detail, but I think that might also be a benefit for cache locality and function call depth when working on the AST. |
@jedel1043 I looked at your suggestion for using a drop guards for the |
Apologies for not responding to the comment earlier!
Yes, we would need some sort of interior mutability, I think the approach with vec and cell wouldn't work because every register needs to have a reference to the allocator, The mechanism is just for debugging purposes and it ensured that registers have the shortest lifespan since you explicitly have to deallocated it, otherwise it was very easy to miss a register drop and essentially wasting a register. Long lifespan registers use more values on the stack and prevents it from being reused. We could change the panic into a debug assert, longer lifespan registers don't cause any weird behaviours, the max they'll do is trigger the call stack error earlier in very recursive calls, just less efficient. EDIT: The double panic could be fixed by checking if we are panic-ing with https://doc.rust-lang.org/std/thread/fn.panicking.html. |
@HalidOdat I was more thinking of using |
Hmmm... I don't think that's going to work, doing a partial borrow on a struct ( We could split the allocator and pass the Am I missing something? |
It would be more like making a |
I think we should probably postpone the disussion until we get some real experience with non persistent registers via the register VM PR. As @HalidOdat mentioned it is more a static safety feature. |
Sounds good! |
da049de
to
c687990
Compare
Rebased after #3942 was merged. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@raskad Amazing work!! This looks pretty much perfect to me! Just some very minor nitpicks :)
TBH I think this is worth doing a release and a blog for it after it's merged, showcasing the performance increase of boa :)
I will see if I can write up a post :) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Really impressive work!
This PR implements binding scopes in the AST and uses those to implement function local variables.
This builds on / inlucdes #3942.
This supersedes #3194.
I appreciate that on first sight this seems like a huge change, so let me break down the changes to make the review easier.
Most of the changes in the AST are the addition of
Scope
s to relevant AST nodes like functions or blocks.Scope
s replaceCompileTimeEnvironment
while moving into the AST. Instead of creating bindings during the bytecode compilation, they are now added after parsing.The big additions to the AST are in
core/ast/src/scope.rs
andcore/ast/src/scope_analyzer.rs
. As expected theScope
is mostly a copy from theCompileTimeEnvironment
. The scope analyzer contains new visitor code that creates bindings and looks for bindings that escape their function scope. The escape analysis is needed so that we can switch bindings to local storage or to the function environments as needed.There are some changes to AST nodes to account for
Scope
s that had to be added. Also I switched all function bodys from usingScript
to their dedicatedFunctionBody
node. That way we can easier work with them in the visitor.Most changes in the parser should only be to account for changes in AST nodes.
Changes in the engine are the addition of usable registers, lifted from #3942. For the creation of local bindings, I had to create additional opcodes, since currently we have no static way of checking for uninitialized bindings. I added some helper functions in the bytecode compiler to create bindings that are either local or non-local.
One additional note is, that the
...Instantiation
functions (GlobalDeclarationInstantiation
and friends) are now split between the AST (creation of bindings) and the bytecode compiler (the rest).There are some significant performance wins that come with this change. I can also imagine some further optimizations based on the static
Scope
analysis that we can do with this.Here are my local benchmarks:
Before:
After: