Skip to content

Conversation

@suwc
Copy link

@suwc suwc commented Jul 13, 2017

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

@suwc
Copy link
Author

suwc commented Jul 13, 2017

Addresses #3064

@suwc suwc requested review from boingoing and pleath July 13, 2017 00:40
@suwc
Copy link
Author

suwc commented Jul 13, 2017

@Microsoft/chakra-developers @Microsoft/chakra-es please review. Thanks!

&prototypeObjectWithProperty))
&prototypeObjectWithProperty)
|| (prototypeObjectWithProperty != nullptr
&& prototypeObjectWithProperty != propertyObject))
Copy link
Contributor

@curtisman curtisman Jul 17, 2017

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.

Copy link
Contributor

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.

Copy link
Author

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)

Copy link
Contributor

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.

@suwc suwc force-pushed the build/suwc/Issue3064 branch from 990d7ec to f2d0e2e Compare July 25, 2017 07:27
@suwc
Copy link
Author

suwc commented Jul 25, 2017

@dotnet-bot test Ubuntu _no_jit_shared_ubuntu_linux_test

@suwc suwc changed the title Fix Issue#3064: TypePropertyCache Miss with propertyObject Fix Issue#3064/#3423: Cache conflicts in super property access Jul 25, 2017
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);
Copy link
Contributor

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......

Copy link
Author

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?

Copy link
Contributor

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

Copy link
Contributor

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?).

Copy link
Contributor

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.)

Copy link
Author

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:

  1. instance is proxy, propertyObject is target object
  2. instance is number, propertyObject is Number.prototype
  3. 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.

  1. and 3) are justified to cache on their prototype (propertyObject).

In reply to: 130143456 [](ancestors = 130143456)

@akroshg
Copy link
Contributor

akroshg commented Jul 29, 2017

so what is a state on this PR?

@pleath
Copy link
Contributor

pleath commented Jul 30, 2017

A simpler fix along the lines of what Curtis suggests seems possible, but we want to do some work to confirm that.

@suwc suwc force-pushed the build/suwc/Issue3064 branch from f2d0e2e to 867e4be Compare August 8, 2017 06:58
@suwc
Copy link
Author

suwc commented Aug 8, 2017

Besides super property access, other cases where propertyObject != instance are following:

  1. instance is proxy, propertyObject is target object
  2. instance is number, propertyObject is Number.prototype
  3. 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.

  1. and 3) are justified to cache on their prototype (propertyObject).
if (opnd->IsSymOpnd() && opnd->AsSymOpnd()->IsPropertySymOpnd())
{
TryOptimizeInstrWithFixedDataProperty(&instr);
if (instr->m_opcode != Js::OpCode::LdSuperFld) // TODO: should profile LdSuperFld?
Copy link
Contributor

@pleath pleath Aug 9, 2017

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


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)
Copy link
Contributor

@pleath pleath Aug 9, 2017

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

Copy link
Author

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)

Copy link
Contributor

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)
Copy link
Contributor

@pleath pleath Aug 9, 2017

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

Copy link
Author

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)

@suwc suwc force-pushed the build/suwc/Issue3064 branch from 867e4be to abba9de Compare August 9, 2017 18:50
@suwc
Copy link
Author

suwc commented Aug 9, 2017

@pleath, latest iteration made LdSuperFld & StSuperFld ProfiledInstr. Also reverted name changes in bytecode emitter to remain generic.


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)
Copy link
Contributor

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.
@suwc suwc force-pushed the build/suwc/Issue3064 branch from abba9de to d2a1cae Compare August 11, 2017 17:15
@suwc
Copy link
Author

suwc commented Aug 11, 2017

@pleath, I reverted the name change. Thanks!

@pleath
Copy link
Contributor

pleath commented Aug 11, 2017

Awesome. Thanks. That leaves us with a nice, simple-looking change.

@chakrabot chakrabot merged commit d2a1cae into chakra-core:release/1.6 Aug 11, 2017
chakrabot pushed a commit that referenced this pull request Aug 11, 2017
…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
chakrabot pushed a commit that referenced this pull request Aug 11, 2017
…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
chakrabot pushed a commit that referenced this pull request Aug 11, 2017
…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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

6 participants