Bug 213545 - [JSC] add IC support for op_get_private_name
Summary: [JSC] add IC support for op_get_private_name
Status: RESOLVED FIXED
Alias: None
Product: WebKit
Classification: Unclassified
Component: New Bugs (show other bugs)
Version: WebKit Nightly Build
Hardware: Unspecified Unspecified
: P2 Normal
Assignee: Caitlin Potter (:caitp)
URL:
Keywords: InRadar
Depends on: 206431 213544
Blocks: 212781 213372 214861
  Show dependency treegraph
 
Reported: 2020-06-23 19:13 PDT by Caitlin Potter (:caitp)
Modified: 2020-07-28 12:29 PDT (History)
8 users (show)

See Also:


Attachments
Patch (27.91 KB, patch)
2020-06-23 19:26 PDT, Caitlin Potter (:caitp)
no flags Details | Formatted Diff | Diff
Patch (30.32 KB, patch)
2020-06-26 14:00 PDT, Caitlin Potter (:caitp)
no flags Details | Formatted Diff | Diff
Patch (30.31 KB, patch)
2020-07-01 14:58 PDT, Caitlin Potter (:caitp)
no flags Details | Formatted Diff | Diff
Patch (23.33 KB, patch)
2020-07-15 13:22 PDT, Caitlin Potter (:caitp)
no flags Details | Formatted Diff | Diff
Patch (23.19 KB, patch)
2020-07-15 13:42 PDT, Caitlin Potter (:caitp)
no flags Details | Formatted Diff | Diff
Patch (23.27 KB, patch)
2020-07-17 13:38 PDT, Caitlin Potter (:caitp)
no flags Details | Formatted Diff | Diff
Patch (23.29 KB, patch)
2020-07-27 17:53 PDT, Caitlin Potter (:caitp)
no flags Details | Formatted Diff | Diff
Patch (29.34 KB, patch)
2020-07-28 11:50 PDT, Caitlin Potter (:caitp)
no flags Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Caitlin Potter (:caitp) 2020-06-23 19:13:30 PDT
[JSC] add IC support for op_get_private_name
Comment 1 Caitlin Potter (:caitp) 2020-06-23 19:26:12 PDT
Created attachment 402618 [details]
Patch
Comment 2 Caitlin Potter (:caitp) 2020-06-26 14:00:39 PDT
Created attachment 402899 [details]
Patch
Comment 3 Caitlin Potter (:caitp) 2020-07-01 14:58:21 PDT
Created attachment 403323 [details]
Patch

Fixes the JSVALUE32_64 builds, but I haven't been able to actually test them yet
Comment 4 Saam Barati 2020-07-13 11:33:24 PDT
Comment on attachment 403323 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=403323&action=review

Mostly LGTM, I agree with the direction here. But I have a suggestion and I also found a bug that we should have a test for.

> Source/JavaScriptCore/bytecode/AccessCase.cpp:268
> +    case LoadPrivate:

name nit: I'd call this "PrivateFieldLoad"

> Source/JavaScriptCore/bytecode/AccessCase.cpp:1437
> +        if (viaProxy() && m_type != LoadPrivate) {

why not have this be an assert inside viaProxy()?

We could just bail on caching if via window proxy.

> Source/JavaScriptCore/bytecode/AccessCase.h:127
> +        LoadPrivate,

Do we really need this to be a different type? It's going down the same path as Load. Why can't all the logic for private fields and cacheablility be done inside Repatch.cpp when deciding if it's ok to cache?

> Source/JavaScriptCore/jit/JITOperations.cpp:2229
> +    if (baseValue.isCell() && baseValue.asCell()->isObject()) {

nit: baseValue.isObject() instead

> Source/JavaScriptCore/jit/JITOperations.cpp:2231
> +        RETURN_IF_EXCEPTION(scope, encodedJSValue());

can this throw in practice? Maybe this should be an EXCEPTION_ASSERT that no exception happened? IIRC, throwing is related to OOM for resolving rope strings.

> Source/JavaScriptCore/jit/JITPropertyAccess.cpp:112
> +        auto notCell = branchIfNotCell(regT0);
> +        notCell.link(this);

not needed

> Source/JavaScriptCore/jit/JITPropertyAccess32_64.cpp:302
> +        auto notCell = branchIfNotCell(regT1);
> +        notCell.link(this);

ditto

> Source/JavaScriptCore/jit/Repatch.cpp:181
> +        return operationGetPrivateName;

this is wrong because the two functions here don't actually have the same signature. This will lead to it being called with the wrong arguments when you repatch. (For comparison, you can look at operationGetById and operationGetByIdOptimize).

Please add a test for this, as it should lead to a crash.
Comment 5 Caitlin Potter (:caitp) 2020-07-13 16:05:20 PDT
Comment on attachment 403323 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=403323&action=review

>> Source/JavaScriptCore/jit/Repatch.cpp:181
>> +        return operationGetPrivateName;
> 
> this is wrong because the two functions here don't actually have the same signature. This will lead to it being called with the wrong arguments when you repatch. (For comparison, you can look at operationGetById and operationGetByIdOptimize).
> 
> Please add a test for this, as it should lead to a crash.

Is there a way I can make this work without having the signature match? It seems like a shame that it needs to match
Comment 6 Saam Barati 2020-07-13 19:14:31 PDT
Comment on attachment 403323 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=403323&action=review

>>> Source/JavaScriptCore/jit/Repatch.cpp:181
>>> +        return operationGetPrivateName;
>> 
>> this is wrong because the two functions here don't actually have the same signature. This will lead to it being called with the wrong arguments when you repatch. (For comparison, you can look at operationGetById and operationGetByIdOptimize).
>> 
>> Please add a test for this, as it should lead to a crash.
> 
> Is there a way I can make this work without having the signature match? It seems like a shame that it needs to match

I mean, there is definitely a way. But it'd be crazy, and require patching way more code than just the call. The only reasonable way is to make them match IMO.

If you think about the machine code, we already have a call laid out. So as part of the ABI, we've prepped the argument registers with the required values. When we switch the call, we rewire just the call instruction (or materialization of constant) to be A or B. To rewire it to a different signature function would require a lot more machinery, that isn't really profitable in any way to do.
Comment 7 Caio Lima 2020-07-15 05:28:36 PDT
Comment on attachment 403323 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=403323&action=review

>> Source/JavaScriptCore/bytecode/AccessCase.h:127
>> +        LoadPrivate,
> 
> Do we really need this to be a different type? It's going down the same path as Load. Why can't all the logic for private fields and cacheablility be done inside Repatch.cpp when deciding if it's ok to cache?

I share the same question. The only difference from `LoadPrivate` and `Load` I can see is the way we print things on `PolymorphicAccess::printInternal`, but I might be missing something.

> Source/JavaScriptCore/jit/JITPropertyAccess.cpp:119
> +            JSValueRegs(regT0), JSValueRegs(regT1), JSValueRegs(regT0));

I think it is very good to have `GPRReg baseGRP = regT0;` and `GPRReg propertyGRP = regT1;` above, so the call here is easier to read as `JITGetByValGenerator gen(..., JSValueRegs(baseGPR), JSValueRegs(propertyGPR), JSValueRegs(regT0));`

> Source/JavaScriptCore/jit/JITPropertyAccess32_64.cpp:309
> +            JSValueRegs::payloadOnly(regT0), JSValueRegs(regT3, regT2), JSValueRegs(regT1, regT0));

ditto
Comment 8 Caio Lima 2020-07-15 10:25:02 PDT
Comment on attachment 403323 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=403323&action=review

> Source/JavaScriptCore/bytecode/AccessCase.cpp:1504
> +        if (m_type == Load || m_type == GetGetter || m_type == LoadPrivate) {

We are missing the inclusion of `m_type == LoadPrivate` above into 32-bits path and this is making we only copy payload when the executing IC's fast path. This endup corrupting the result of `get_private_name`, which is triggering some segmentation faults into ARMv7 and MIPS (they are reported on last EWS run, BTW).
Comment 9 Caio Lima 2020-07-15 11:07:40 PDT
Comment on attachment 403323 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=403323&action=review

>> Source/JavaScriptCore/jit/JITPropertyAccess.cpp:112
>> +        notCell.link(this);
> 
> not needed

I'm not sure if we would like to have this path here. IIRC, this was added as a mitigation into get_by_val IC due to some performance regression into JetStream 2 or Speedometer 2, but I'm not sure if this would be necessary for private fields. It will only be possible to take this path if we have multiple evaluations of class and this might be a unlikely case IMHO. We might decide to add it later if we identify that multiple class evaluation is a common pattern.
Comment 10 Caitlin Potter (:caitp) 2020-07-15 13:22:40 PDT
Created attachment 404383 [details]
Patch
Comment 11 Caitlin Potter (:caitp) 2020-07-15 13:28:56 PDT
Comment on attachment 403323 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=403323&action=review

I've found some minor problems with the current patch so far, including some mentioned here. Nothing significant though.

>> Source/JavaScriptCore/bytecode/AccessCase.cpp:268
>> +    case LoadPrivate:
> 
> name nit: I'd call this "PrivateFieldLoad"

I've removed the LoadPrivate enum value instead, as requested below

>> Source/JavaScriptCore/bytecode/AccessCase.cpp:1437
>> +        if (viaProxy() && m_type != LoadPrivate) {
> 
> why not have this be an assert inside viaProxy()?
> 
> We could just bail on caching if via window proxy.

done

>>> Source/JavaScriptCore/bytecode/AccessCase.h:127
>>> +        LoadPrivate,
>> 
>> Do we really need this to be a different type? It's going down the same path as Load. Why can't all the logic for private fields and cacheablility be done inside Repatch.cpp when deciding if it's ok to cache?
> 
> I share the same question. The only difference from `LoadPrivate` and `Load` I can see is the way we print things on `PolymorphicAccess::printInternal`, but I might be missing something.

It doesn't really need to be a different type in practice, but it seemed nice to have a different descriptive string for it. I've removed it.

>> Source/JavaScriptCore/jit/JITOperations.cpp:2229
>> +    if (baseValue.isCell() && baseValue.asCell()->isObject()) {
> 
> nit: baseValue.isObject() instead

done

>> Source/JavaScriptCore/jit/JITOperations.cpp:2231
>> +        RETURN_IF_EXCEPTION(scope, encodedJSValue());
> 
> can this throw in practice? Maybe this should be an EXCEPTION_ASSERT that no exception happened? IIRC, throwing is related to OOM for resolving rope strings.

no, in practice it should be impossible

--- oops, I haven't changed this yet...

>>> Source/JavaScriptCore/jit/JITPropertyAccess.cpp:112
>>> +        notCell.link(this);
>> 
>> not needed
> 
> I'm not sure if we would like to have this path here. IIRC, this was added as a mitigation into get_by_val IC due to some performance regression into JetStream 2 or Speedometer 2, but I'm not sure if this would be necessary for private fields. It will only be possible to take this path if we have multiple evaluations of class and this might be a unlikely case IMHO. We might decide to add it later if we identify that multiple class evaluation is a common pattern.

it's gone

>> Source/JavaScriptCore/jit/JITPropertyAccess.cpp:119
>> +            JSValueRegs(regT0), JSValueRegs(regT1), JSValueRegs(regT0));
> 
> I think it is very good to have `GPRReg baseGRP = regT0;` and `GPRReg propertyGRP = regT1;` above, so the call here is easier to read as `JITGetByValGenerator gen(..., JSValueRegs(baseGPR), JSValueRegs(propertyGPR), JSValueRegs(regT0));`

Done... I'm undecided on if it's actually better or not though 👀

>>>> Source/JavaScriptCore/jit/Repatch.cpp:181
>>>> +        return operationGetPrivateName;
>>> 
>>> this is wrong because the two functions here don't actually have the same signature. This will lead to it being called with the wrong arguments when you repatch. (For comparison, you can look at operationGetById and operationGetByIdOptimize).
>>> 
>>> Please add a test for this, as it should lead to a crash.
>> 
>> Is there a way I can make this work without having the signature match? It seems like a shame that it needs to match
> 
> I mean, there is definitely a way. But it'd be crazy, and require patching way more code than just the call. The only reasonable way is to make them match IMO.
> 
> If you think about the machine code, we already have a call laid out. So as part of the ABI, we've prepped the argument registers with the required values. When we switch the call, we rewire just the call instruction (or materialization of constant) to be A or B. To rewire it to a different signature function would require a lot more machinery, that isn't really profitable in any way to do.

My mistake, I didn't notice I was using this in 2 cases --- one where it worked (megamorphic before baseline JIT) and switching to megamorphic in the JIT slow path.

I've added a StructureStub parameter to the generic case, and just have it pass null for it if no StubInfo will be created (as opposed to having 2 generic variants, like GetByVal has)

I'll add an explicit test to make sure both paths are hit.

----

I've added a test since writing this draft, and talked with Caio offline about this... I haven't been able to actually get the IC system to completely give up on caching (and repatch with `operationGetPrivateName` instead of `operationGetPrivateNameOptimize`), so the only way I've been able to reproduce it is with --forceICFailure. This seems like a problem, but I'm not sure if it's with my changes to `tryCacheGetBy` or something upstream

> Source/JavaScriptCore/jit/Repatch.cpp:256
> +            if (baseCell->type() == PureForwardingProxyType && !isPrivate) {

I had tried adding `if (isPrivate) return GiveUpOnCache;` but the branch was never taken, even when the base object is `globalThis`.

I really have no idea :(
Comment 12 Caitlin Potter (:caitp) 2020-07-15 13:42:57 PDT
Created attachment 404389 [details]
Patch

fixes the build, I think/hope. Not sure why cmake builds aren't erroring on unused variables ��
Comment 13 Caitlin Potter (:caitp) 2020-07-15 15:02:05 PDT
I've fixed the C-linkage-for-C++-function compiler error locally.
Comment 14 Caitlin Potter (:caitp) 2020-07-17 13:38:31 PDT
Created attachment 404586 [details]
Patch
Comment 15 Caitlin Potter (:caitp) 2020-07-27 17:53:13 PDT
Created attachment 405334 [details]
Patch
Comment 16 Saam Barati 2020-07-28 10:13:44 PDT
Comment on attachment 405334 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=405334&action=review

Mostly LGTM, just a few comments.

> Source/JavaScriptCore/jit/JITOperations.cpp:2231
> +    // Only consider caching if the base value doesn't need to be converted to a JSObject

nit: this comment is not needed given the code is doing what the comment says

Question: should we invalidate the opportunity to cache if we ever see non-object?

> Source/JavaScriptCore/jit/JITOperations.cpp:2237
> +        JSObject* base = baseValue.asCell()->toObject(globalObject);

this should be a jsCast<JSObject*>

> Source/JavaScriptCore/jit/JITOperations.cpp:2260
> +    auto scope = DECLARE_THROW_SCOPE(vm);

nit: You can remove this.

> Source/JavaScriptCore/jit/JITOperations.cpp:2267
> +    RELEASE_AND_RETURN(scope, JSValue::encode(getPrivateName(globalObject, callFrame, baseValue, fieldNameValue)));

and just turn this into a normal return.

> Source/JavaScriptCore/jit/JITPropertyAccess.cpp:115
> +    gen.stubInfo()->accessType = AccessType::GetPrivateName;

nit: This is a bit fragile. It's relying on SSI's constructor not looking at this access type. Why not have JITGetByValGenerator take an AccessType as a parameter?

> Source/JavaScriptCore/jit/JITPropertyAccess32_64.cpp:306
> +    gen.stubInfo()->accessType = AccessType::GetPrivateName;

ditto

> Source/JavaScriptCore/jit/Repatch.cpp:385
> +                    ASSERT(!slot.isUnset());

let's RELEASE_ASSERT

> Source/JavaScriptCore/jit/Repatch.cpp:386
> +                    constexpr bool cannotBeProxy = false;

This name has a negative that it should not. I'd do:
"constexpr bool isProxy = false;"
Comment 17 Caitlin Potter (:caitp) 2020-07-28 11:50:36 PDT
Created attachment 405385 [details]
Patch
Comment 18 Caitlin Potter (:caitp) 2020-07-28 11:53:41 PDT
Comment on attachment 405334 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=405334&action=review

Thanks for the review, Saam

>> Source/JavaScriptCore/jit/JITOperations.cpp:2231
>> +    // Only consider caching if the base value doesn't need to be converted to a JSObject
> 
> nit: this comment is not needed given the code is doing what the comment says
> 
> Question: should we invalidate the opportunity to cache if we ever see non-object?

I'm not sure. I'm imagining an application which primarily falls on the fast path, but rarely takes the slow path due to a primitive. It would be a shame to eliminate the fast path if the primitive case is taken rarely. But maybe it does make sense to do that.

Anyways, comment removed.

>> Source/JavaScriptCore/jit/JITOperations.cpp:2237
>> +        JSObject* base = baseValue.asCell()->toObject(globalObject);
> 
> this should be a jsCast<JSObject*>

done

>> Source/JavaScriptCore/jit/JITOperations.cpp:2260
>> +    auto scope = DECLARE_THROW_SCOPE(vm);
> 
> nit: You can remove this.

done and done

>> Source/JavaScriptCore/jit/JITPropertyAccess.cpp:115
>> +    gen.stubInfo()->accessType = AccessType::GetPrivateName;
> 
> nit: This is a bit fragile. It's relying on SSI's constructor not looking at this access type. Why not have JITGetByValGenerator take an AccessType as a parameter?

Done and done

>> Source/JavaScriptCore/jit/Repatch.cpp:385
>> +                    ASSERT(!slot.isUnset());
> 
> let's RELEASE_ASSERT

done

>> Source/JavaScriptCore/jit/Repatch.cpp:386
>> +                    constexpr bool cannotBeProxy = false;
> 
> This name has a negative that it should not. I'd do:
> "constexpr bool isProxy = false;"

done
Comment 19 Saam Barati 2020-07-28 12:14:55 PDT
Comment on attachment 405385 [details]
Patch

Nice. r=me
Comment 20 EWS 2020-07-28 12:28:21 PDT
Committed r265000: <https://trac.webkit.org/changeset/265000>

All reviewed patches have been landed. Closing bug and clearing flags on attachment 405385 [details].
Comment 21 Radar WebKit Bug Importer 2020-07-28 12:29:18 PDT
<rdar://problem/66231520>