- Notifications
You must be signed in to change notification settings - Fork 1.2k
Fix Issue#3064/#3423: Cache conflicts in super property access #3336
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
| Addresses #3064 |
| @Microsoft/chakra-developers @Microsoft/chakra-es please review. Thanks! |
| &prototypeObjectWithProperty)) | ||
| &prototypeObjectWithProperty) | ||
| || (prototypeObjectWithProperty != nullptr | ||
| && prototypeObjectWithProperty != propertyObject)) |
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.
Huh? prototypeObjectWithProperty should never be the propertyObject itself. Otherwise, we will not hit the "proto" inline cache and have prototypeObjectWithProperty not null. There is probably something else wrong.
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.
@suwc , as we discussed, I think we're probably abusing the cache on a byte code level. I'll make some time to investigate today. If that's the case, then we want to fix the code gen rather than working around it in the cache logic.
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.
The TypePropertyCache can be populated in multiple occasions. In this repro case, calls to “super.x” go through these lines in GetProperty_Internal():
PropertyValueInfo::Set(info, requestContext->GetLibrary()->GetMissingPropertyHolder(), 0); CacheOperators::CachePropertyRead(instance, requestContext->GetLibrary()->GetMissingPropertyHolder(), isRoot, propertyId, true, info, requestContext); Now that the cache is populated with requestContext->GetLibrary()->GetMissingPropertyHolder() as “prototypeObjectWithProperty”, next call to “this.x” (having same “instance”) will be a cache hit (with prototypeObjectWithProperty != nullptr), whereas it should be a cache miss + update.
In reply to: 127714797 [](ancestors = 127714797)
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.
Yes, as I thought, the byte code gen for super.x is misusing the inline caches. So let's apply a fix there rather than in the cache logic. We can discuss offline.
990d7ec to f2d0e2e Compare | @dotnet-bot test Ubuntu _no_jit_shared_ubuntu_linux_test |
| if (value && !requestContext->IsUndeclBlockVar(*value) && !WithScopeObject::Is(object)) | ||
| { | ||
| CacheOperators::CachePropertyRead(instance, object, isRoot, propertyId, false, info, requestContext); | ||
| CacheOperators::CachePropertyRead(isSuper ? propertyObject : instance, object, isRoot, propertyId, false, info, requestContext); |
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.
I think this should always be propertyObject......
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.
@curtisman I don't have an example where caching on instance is more appropriate when propertyObject != instance. When a super property ("super.x") exists in super object's prototype (vs. the super object itself), it is distinguished by object != prototypeObject. It would be simpler if we can go without 'isSuper'.
@pleath am I missing something?
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.
You should be able to find all the cases that caching on the instance is wrong and create test cases for them
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.
If we can prove that there's no case where propertyObject != instance, and where we want to cache based on instance, or where the fact that propertyObject !=instance is our indication that we have a prototype access, then going without isSuper seems fine. The only case I can think of is where instance is a tagged number, and in that case it seems fine to cache as a local access to Number.prototype (which will be propertyObject, right?).
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.
(All of which means that the logic we've had since IE9 is subtly wrong, but in a way that never mattered until super was implemented. That may be true, but it would be nice to prove it.)
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.
Besides super property access, other cases where propertyObject != instance are following:
- instance is proxy, propertyObject is target object
- instance is number, propertyObject is Number.prototype
- instance is boolean, propertyObject is Boolean.prototype
By design, 1) does not cache property access (confirmed) because underlying target object can mutate without proxy knowing it.
- and 3) are justified to cache on their prototype (propertyObject).
In reply to: 130143456 [](ancestors = 130143456)
| so what is a state on this PR? |
| A simpler fix along the lines of what Curtis suggests seems possible, but we want to do some work to confirm that. |
f2d0e2e to 867e4be Compare | Besides super property access, other cases where propertyObject != instance are following:
By design, 1) does not cache property access (confirmed) because underlying target object can mutate without proxy knowing it.
|
lib/Backend/GlobOpt.cpp Outdated
| if (opnd->IsSymOpnd() && opnd->AsSymOpnd()->IsPropertySymOpnd()) | ||
| { | ||
| TryOptimizeInstrWithFixedDataProperty(&instr); | ||
| if (instr->m_opcode != Js::OpCode::LdSuperFld) // TODO: should profile LdSuperFld? |
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.
Yes, I would prefer to profile LdSuperFld. It seems best to let it participate in this optimization. If there's a reason we can't do that, I'd prefer to use some kind of general check on the opcode attributes rather than special-casing for this opcode. #Pending
lib/Backend/JnHelperMethodList.h Outdated
| | ||
| HELPERCALL(Op_PatchGetValue, ((Js::Var (*)(Js::FunctionBody *const, Js::InlineCache *const, const Js::InlineCacheIndex, Js::Var, Js::PropertyId))Js::JavascriptOperators::PatchGetValue<true, Js::InlineCache>), AttrCanThrow) | ||
| HELPERCALL(Op_PatchGetValueWithThisPtr, ((Js::Var(*)(Js::FunctionBody *const, Js::InlineCache *const, const Js::InlineCacheIndex, Js::Var, Js::PropertyId, Js::Var))Js::JavascriptOperators::PatchGetValueWithThisPtr<true, Js::InlineCache>), AttrCanThrow) | ||
| HELPERCALL(Op_PatchGetSuperValue, ((Js::Var(*)(Js::FunctionBody *const, Js::InlineCache *const, const Js::InlineCacheIndex, Js::Var, Js::PropertyId, Js::Var, const bool))Js::JavascriptOperators::PatchGetSuperValue<true, Js::InlineCache>), AttrCanThrow) |
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.
Curious...is there a reason for the name change? I kind of like the generic name better than one that suggests this is only for super. But I could be convinced. #Resolved
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.
This is just a wrapper. I renamed the generic version to "PatchGetValueHelper" and hid it from JIT, in order to have (current and future) flexibility of adding, changing, or removing a parameter without JIT impact. Same goes for other helper functions. JIT doesn't need to deal with these.
In reply to: 132067176 [](ancestors = 132067176)
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.
Well, the JIT isn't really a factor. I'm just wondering why we wouldn't go with a generic name -- or, specifically, why we would change from using a generic name to using a less portable one. Can we revert this?
| } | ||
| | ||
| void ByteCodeWriter::PatchablePropertyWithThisPtr(OpCode op, RegSlot value, RegSlot instance, RegSlot thisInstance, uint cacheId, bool isCtor, bool registerCacheIdForCall) | ||
| void ByteCodeWriter::PatchableSuperProperty(OpCode op, RegSlot value, RegSlot instance, RegSlot thisInstance, uint cacheId, bool isCtor, bool registerCacheIdForCall) |
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.
Again, I generally prefer a generic name. #Pending
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.
Although this so far is used only for super, I can see a point for leaving it in a generic name here. I will revert it to the old name.
In reply to: 132067519 [](ancestors = 132067519)
867e4be to abba9de Compare | @pleath, latest iteration made LdSuperFld & StSuperFld ProfiledInstr. Also reverted name changes in bytecode emitter to remain generic. |
lib/Backend/JnHelperMethodList.h Outdated
| | ||
| HELPERCALL(Op_PatchPutValue, ((void (*)(Js::FunctionBody *const, Js::InlineCache *const, const Js::InlineCacheIndex, Js::Var, Js::PropertyId, Js::Var, Js::PropertyOperationFlags))Js::JavascriptOperators::PatchPutValue<true, Js::InlineCache>), AttrCanThrow) | ||
| HELPERCALL(Op_PatchPutValueWithThisPtr, ((void (*)(Js::FunctionBody *const, Js::InlineCache *const, const Js::InlineCacheIndex, Js::Var, Js::PropertyId, Js::Var, Js::Var, Js::PropertyOperationFlags))Js::JavascriptOperators::PatchPutValueWithThisPtr<true, Js::InlineCache>), AttrCanThrow) | ||
| HELPERCALL(Op_PatchPutSuperValue, ((void (*)(Js::FunctionBody *const, Js::InlineCache *const, const Js::InlineCacheIndex, Js::Var, Js::PropertyId, Js::Var, Js::Var, Js::PropertyOperationFlags))Js::JavascriptOperators::PatchPutSuperValue<true, Js::InlineCache>), AttrCanThrow) |
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.
Ditto for these name changes. The existing names are preferable, IMO, because they describe what the method does and don't create the impression that the methods are necessarily limited to one opcode.
…cess Accesses to super properties are cached on 'this' object (vs. the "super" object), causing conflict between e.g. super.x and this.x. Similar conflicts cause Issue#3423 for GetProperty cases. Fix by changing to object/propertyObject for caching super property accesses.
abba9de to d2a1cae Compare | @pleath, I reverted the name change. Thanks! |
| Awesome. Thanks. That leaves us with a nice, simple-looking change. |
…operty access Merge pull request #3336 from suwc:build/suwc/Issue3064 Accesses to super properties are cached on 'this' object (vs. the "super" object), causing conflict between e.g. super.x and this.x. Similar conflicts cause Issue#3423 for GetProperty cases. Fix by adding 'isSuper' flag to use appropriate object for caching. Fixes #3064, Fixes #3423
…n super property access Merge pull request #3336 from suwc:build/suwc/Issue3064 Accesses to super properties are cached on 'this' object (vs. the "super" object), causing conflict between e.g. super.x and this.x. Similar conflicts cause Issue#3423 for GetProperty cases. Fix by adding 'isSuper' flag to use appropriate object for caching. Fixes #3064, Fixes #3423
…e conflicts in super property access Merge pull request #3336 from suwc:build/suwc/Issue3064 Accesses to super properties are cached on 'this' object (vs. the "super" object), causing conflict between e.g. super.x and this.x. Similar conflicts cause Issue#3423 for GetProperty cases. Fix by adding 'isSuper' flag to use appropriate object for caching. Fixes #3064, Fixes #3423
Accesses to super properties are cached on 'this' object (vs. the
"super" object), causing conflict between e.g. super.x and this.x.
Similar conflicts cause Issue#3423 for GetProperty cases.
Fix by adding 'isSuper' flag to use appropriate object for caching.
Fixes #3064, Fixes #3423