Skip to content

avm2: Use return type of methods in optimizer #20257

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

Open
wants to merge 43 commits into
base: master
Choose a base branch
from

Conversation

Lord-McSweeney
Copy link
Collaborator

@Lord-McSweeney Lord-McSweeney commented Apr 24, 2025

Depends on #20233

Closes #17495

We now verify that overrides of methods use the same return type as the method they override, which allows us to make use of the return type of all methods in the optimizer.

Box2d stats:

ConstructSuper -> Pop: 70%

FindPropStrict -> GetScriptGlobals: 82.71%
FindPropStrict -> GetOuterScope: 9%
FindPropStrict -> GetScopeObject: 8.28%

FindProperty -> GetOuterScope: 5.66%
FindProperty -> GetScopeObject: 94.33%

InitProperty -> SetSlot: 6.3% (-2.19%)
InitProperty -> SetSlotNoCoerce: 90.95% (+2.19%)
InitProperty -> CallMethod: 2.73%

SetProperty -> SetSlot: 8.07% (-1.72%)
SetProperty -> SetSlotNoCoerce: 73.88% (+1.72%)
SetProperty -> CallMethod: 2.4%

GetProperty -> GetSlot: 88.89%
GetProperty -> CallMethod: 0.47%

CallProperty -> CallMethod: 83.26%
CallProperty -> CoerceUSwapPop: 5.17%
CallProperty -> CoerceISwapPop: 9.56%

CallPropVoid -> CallMethod: 93.93% (+2.65%)

ConstructProp -> ConstructSlot: 100%

Coerce -> Nop: 73.18% (+5.93%)

CoerceD -> Nop: 98.75% (+8.71%)

CoerceB -> Nop: 97.29% (+70.27%)

CoerceU -> Nop: 64.91% (+2.63%)

CoerceI -> Nop: 52% (+5%)

ReturnValue -> ReturnValueNoCoerce: 96.39% (+12.61%)

@Lord-McSweeney Lord-McSweeney added A-avm2 Area: AVM2 (ActionScript 3) T-perf Type: Performance Improvements labels Apr 24, 2025
@Lord-McSweeney Lord-McSweeney force-pushed the avm2-use-return-type branch 3 times, most recently from a9c372e to 1ae6596 Compare May 2, 2025 01:21
Lord-McSweeney added 25 commits May 1, 2025 18:53
let my_name = instance_trait.name();

let names_match = super_name.local_name() == my_name.local_name()
&& (super_name.namespace().matches_ns(my_name.namespace())
Copy link
Collaborator

@adrian17 adrian17 May 5, 2025

Choose a reason for hiding this comment

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

Why not do it during (or after) init_vtable? It already contains the "check trait kind" logic. If we did it during init_vtable, we could directly compare method_table[*disp_id as usize] just before setting the override. If we did it after init_vtable, we could lookup the name in resolved_traits instead. Both feel faster and more correct than quadratic loop with name comparisons.

Actually...

I was just checking what FP does and turns out it does both: the trait kind check early and signature check late - and it can be observed.

The "is it legal to override trait kind A with B" and "overriding methods without override is illegal" checks are done early during initial class loading, preparing the main name->property hashmap; FP does this eagerly, while we do init_vtable late - so in FP the class can fail these even if it's never used.

(see Traits::getOverride and where it's used. It does do a lookup on parent by name just like we do match resolved_traits.get() in init_vtable.)

Meanwhile the overridden methods' signatures are compared late, like in newclass. So if the class exists and has a mismatched signature but is never used/referred to, there won't be any VerifyErrors.

(see TraitsBindings::checkOverride, used in VTable::resolveSignatures(self), which is directly called from MethodEnv::newclass (and a dozen other places). It also doesn't concern itself with names anymore, it just compares vtable.methods[dispid] vs parent_vtable.methods[dispid]).

Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm not saying we need to do it exactly like FP; simply moving the entire logic during/after init_vtable will just prevent some quadratic loops and should be conceptually simpler overall.

Copy link
Collaborator

Choose a reason for hiding this comment

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

And even if the verification didn't move and stayed before init_vtable, couldn't we still lookup the superclass's fields by name without a loop, just like init_vtable does: resolved_traits.get(trait_data.name())?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-avm2 Area: AVM2 (ActionScript 3) T-perf Type: Performance Improvements
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants