WebKit Bugzilla
New
Browse
Log In
×
Sign in with GitHub
or
Remember my login
Create Account
·
Forgot Password
Forgotten password account recovery
RESOLVED FIXED
196886
DFG should be able to constant fold Object.create() with a constant prototype operand
https://bugs.webkit.org/show_bug.cgi?id=196886
Summary
DFG should be able to constant fold Object.create() with a constant prototype...
Robin Morisset
Reported
2019-04-12 17:37:31 PDT
It is a fairly simple and limited patch, as it only works when the DFG can prove the exact object used as prototype. But when it applies it can be a significant win: Baseline Optim object-create-constant-prototype 3.6082+-0.0979 ^ 1.6947+-0.0756 ^ definitely 2.1292x faster object-create-null 11.4492+-0.2510 ? 11.5030+-0.2402 ? object-create-unknown-object-prototype 15.6067+-0.1851 ? 15.7500+-0.2322 ? object-create-untyped-prototype 8.8873+-0.1240 ? 8.9806+-0.1202 ? might be 1.0105x slower <geometric> 8.6967+-0.1208 ^ 7.2408+-0.1367 ^ definitely 1.2011x faster The only subtlety is that we need to to access the StructureCache concurrently from the compiler thread (see
https://bugs.webkit.org/show_bug.cgi?id=186199
) I solved this with a simple lock, taken when the compiler thread tries to read it, and when the main thread tries to modify it. I expect it to be extremely low contention, but will watch the bots just in case. The lock is taken neither when the main thread is only reading the cache (it has no-one to race with), nor when the GC purges it of dead entries (it does not free anything while a compiler thread is in the middle of a phase).
Attachments
Patch
(11.34 KB, patch)
2019-04-12 17:44 PDT
,
Robin Morisset
ysuzuki
: review+
Details
Formatted Diff
Diff
Patch
(11.98 KB, patch)
2019-04-15 11:15 PDT
,
Robin Morisset
no flags
Details
Formatted Diff
Diff
Patch
(12.78 KB, patch)
2019-04-15 12:42 PDT
,
Robin Morisset
no flags
Details
Formatted Diff
Diff
Patch
(12.78 KB, patch)
2019-04-15 13:11 PDT
,
Robin Morisset
saam
: commit-queue-
Details
Formatted Diff
Diff
PatchForLanding
(12.77 KB, patch)
2019-04-15 16:22 PDT
,
Robin Morisset
no flags
Details
Formatted Diff
Diff
Show Obsolete
(4)
View All
Add attachment
proposed patch, testcase, etc.
Robin Morisset
Comment 1
2019-04-12 17:44:22 PDT
Created
attachment 367361
[details]
Patch
Yusuke Suzuki
Comment 2
2019-04-12 18:48:30 PDT
Comment on
attachment 367361
[details]
Patch View in context:
https://bugs.webkit.org/attachment.cgi?id=367361&action=review
Looks nice. r=me with nits.
> Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h:2595 > + else { > + RELEASE_ASSERT(base.isObject());
Since this child1 is `UntypedUse`, any value can come here. Let's do `else if (base.isObject())` instead. And initialize `structure` with nullptr since we now have a path that does not assign anything to the variable structure :) I think we can write a test, like, function test() { for (var i = 0; i < 1e6; ++i) { try { Object.create(undefined); } catch { } } }
> Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h:2599 > + if (!!structure) {
Personally I like `if (structure)`.
> Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp:759 > + RELEASE_ASSERT(base.isObject());
Ditto.
> Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp:763 > + if (!!structure) {
Ditto.
> Source/JavaScriptCore/runtime/StructureCache.cpp:63 > + holdLock(m_lock);
This immediately discards the LockHolder after this statement finishes, and unlocked. Use `auto locker = holdLock(m_lock);` instead.
> Source/JavaScriptCore/runtime/StructureCache.cpp:74 > + holdLock(m_lock);
Ditto.
> Source/JavaScriptCore/runtime/StructureCache.h:61 > StructureMap m_structures;
This is WeakGCMap. So it is held by Heap, and collector checks this without taking this lock. I think it is safe since we don't run GC end phase while compiler thread is running. Another thing I would like to ensure is that this holds Weak<> handles. I think this does not return dead cells when the compiler threads successfully read it concurrently. Can you ensure they are safe?
EWS Watchlist
Comment 3
2019-04-15 09:31:45 PDT
Attachment 367361
[details]
did not pass style-queue: ERROR: Source/JavaScriptCore/runtime/StructureCache.h:55: The parameter name "globalObject" adds no information, so it should be removed. [readability/parameter_name] [5] Total errors found: 1 in 7 files If any of these errors are false positives, please file a bug against check-webkit-style.
Saam Barati
Comment 4
2019-04-15 09:33:02 PDT
Comment on
attachment 367361
[details]
Patch View in context:
https://bugs.webkit.org/attachment.cgi?id=367361&action=review
> Source/JavaScriptCore/runtime/StructureCache.cpp:68 > +Structure* StructureCache::tryEmptyObjectStructureForPrototypeFromCompilerThread(JSGlobalObject* globalObject, JSObject* prototype, unsigned inlineCapacity)
Name nit: maybe “tryGetEmpty...”?
Saam Barati
Comment 5
2019-04-15 09:49:55 PDT
Comment on
attachment 367361
[details]
Patch r=me too with Yusuke’s comments addressed
Robin Morisset
Comment 6
2019-04-15 11:08:00 PDT
Thanks for the review!
> > > Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h:2595 > > + else { > > + RELEASE_ASSERT(base.isObject()); > > Since this child1 is `UntypedUse`, any value can come here. Let's do `else > if (base.isObject())` instead. > And initialize `structure` with nullptr since we now have a path that does > not assign anything to the variable structure :) > > I think we can write a test, like, > > function test() > { > for (var i = 0; i < 1e6; ++i) { > try { Object.create(undefined); } catch { } > } > }
Done. I've added a couple lines to JSTests/stress/object-create-undefined.js to that effect.
> > > Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h:2599 > > + if (!!structure) { > > Personally I like `if (structure)`.
Fixed.
> > Source/JavaScriptCore/runtime/StructureCache.cpp:63 > > + holdLock(m_lock); > > This immediately discards the LockHolder after this statement finishes, and > unlocked. > Use `auto locker = holdLock(m_lock);` instead.
Oops, that was a big mistake on my part. Thank you for catching it. I will add WARN_UNUSED_RESULT to holdLock in a separate patch.
> > Source/JavaScriptCore/runtime/StructureCache.h:61 > > StructureMap m_structures; > > This is WeakGCMap. So it is held by Heap, and collector checks this without > taking this lock. > I think it is safe since we don't run GC end phase while compiler thread is > running. > Another thing I would like to ensure is that this holds Weak<> handles. > I think this does not return dead cells when the compiler threads > successfully read it concurrently. > Can you ensure they are safe?
I don't understand what you mean here. The main thread outside of GC can only add things to the cache, not remove them. The compiler thread can never race with the GC end phase, as it only runs between compiler phases. And the lock ensures that additions to the cache are atomic to the compiler thread (it cannot see a half-written value). So I don't see how the compiler could ever read stuff that has not been properly written into the cache, and since it is a WeakGCMap, they are automatically Structure* referred by a Weak<> handle. We then registerStructure them, making sure they won't be GC-ed even after our phase ends. What do you mean by "safe"?
Robin Morisset
Comment 7
2019-04-15 11:11:07 PDT
(In reply to Saam Barati from
comment #4
)
> Comment on
attachment 367361
[details]
> Patch > > View in context: >
https://bugs.webkit.org/attachment.cgi?id=367361&action=review
> > > Source/JavaScriptCore/runtime/StructureCache.cpp:68 > > +Structure* StructureCache::tryEmptyObjectStructureForPrototypeFromCompilerThread(JSGlobalObject* globalObject, JSObject* prototype, unsigned inlineCapacity) > > Name nit: maybe “tryGetEmpty...”?
The webkit style guide (
https://webkit.org/code-style-guidelines/#names
) recommends only using the "Get" verb for functions that return their result through an argument: 'Use bare words for getters [..] Precede getters that return values through out arguments with the word “get”.' But I agree that the current name is a monstrosity, I was just unsure what better name to pick.
Robin Morisset
Comment 8
2019-04-15 11:15:59 PDT
Created
attachment 367431
[details]
Patch
Saam Barati
Comment 9
2019-04-15 11:34:19 PDT
(In reply to Robin Morisset from
comment #7
)
> (In reply to Saam Barati from
comment #4
) > > Comment on
attachment 367361
[details]
> > Patch > > > > View in context: > >
https://bugs.webkit.org/attachment.cgi?id=367361&action=review
> > > > > Source/JavaScriptCore/runtime/StructureCache.cpp:68 > > > +Structure* StructureCache::tryEmptyObjectStructureForPrototypeFromCompilerThread(JSGlobalObject* globalObject, JSObject* prototype, unsigned inlineCapacity) > > > > Name nit: maybe “tryGetEmpty...”? > > The webkit style guide (
https://webkit.org/code-style-guidelines/#names
) > recommends only using the "Get" verb for functions that return their result > through an argument: 'Use bare words for getters [..] Precede getters that > return values through out arguments with the word “get”.' > > But I agree that the current name is a monstrosity, I was just unsure what > better name to pick.
I would ignore the style guide here given that the current name doesn't resemble anything sensible and we probably ignore it in other places. Alternately, we've picked names like this for stuff like this in the past: "emptyObjectStructureForPrototypeConcurrently" I think that's good enough.
Saam Barati
Comment 10
2019-04-15 11:35:16 PDT
Comment on
attachment 367431
[details]
Patch View in context:
https://bugs.webkit.org/attachment.cgi?id=367431&action=review
> Source/JavaScriptCore/runtime/StructureCache.cpp:62 > + // We do need to lock here to avoid a race with a compiler thread that may be accessing the cache in read-only mode.
No need for this comment, it's obvious.
> Source/JavaScriptCore/runtime/StructureCache.cpp:73 > + // We need to hold a lock to make sure the main thread is not modifying the underlying hash table from under us.
ditto
EWS Watchlist
Comment 11
2019-04-15 11:56:44 PDT
Attachment 367431
[details]
did not pass style-queue: ERROR: Source/JavaScriptCore/runtime/StructureCache.h:55: The parameter name "globalObject" adds no information, so it should be removed. [readability/parameter_name] [5] Total errors found: 1 in 8 files If any of these errors are false positives, please file a bug against check-webkit-style.
Yusuke Suzuki
Comment 12
2019-04-15 12:02:20 PDT
Comment on
attachment 367361
[details]
Patch View in context:
https://bugs.webkit.org/attachment.cgi?id=367361&action=review
>> Source/JavaScriptCore/runtime/StructureCache.h:61 >> StructureMap m_structures; > > This is WeakGCMap. So it is held by Heap, and collector checks this without taking this lock. > I think it is safe since we don't run GC end phase while compiler thread is running. > Another thing I would like to ensure is that this holds Weak<> handles. > I think this does not return dead cells when the compiler threads successfully read it concurrently. > Can you ensure they are safe?
Sorry, I'll simplify the questions. Can you answer the following questions? 1. WeakGCMap is registered to the Heap, and GC repeatedly looks into it. But at that time, collector does not take this m_lock. Is it safe? 2. WeakGCMap holds Weak<>, and concurrent compiler reads cells from it. Do concurrent compilers read the live cells or nullptr from this Weak<> handles?
Robin Morisset
Comment 13
2019-04-15 12:42:02 PDT
Created
attachment 367441
[details]
Patch Went with tryEmptyObjectStructureConcurrently, it is significantly shorter, respect the style guide, and still makes it clear that it can fail. The 'ForPrototype' part of the name is less important in my view, considering that there is an argument called prototype, it should be sufficiently clear. Also removed the comments that Saam finds obvious, and updated the year in the top-of-file copyright comments.
Saam Barati
Comment 14
2019-04-15 12:45:29 PDT
(In reply to Robin Morisset from
comment #13
)
> Created
attachment 367441
[details]
> Patch > > Went with tryEmptyObjectStructureConcurrently, it is significantly shorter, > respect the style guide, and still makes it clear that it can fail. > The 'ForPrototype' part of the name is less important in my view, > considering that there is an argument called prototype, it should be > sufficiently clear.
I still don't like that name. I'm not sure what our style guideline says here, but I think we tend to not choose names like this that would make a grammatically incorrect sentence.
> > Also removed the comments that Saam finds obvious, and updated the year in > the top-of-file copyright comments.
Saam Barati
Comment 15
2019-04-15 12:48:19 PDT
(In reply to Saam Barati from
comment #14
)
> (In reply to Robin Morisset from
comment #13
) > > Created
attachment 367441
[details]
> > Patch > > > > Went with tryEmptyObjectStructureConcurrently, it is significantly shorter, > > respect the style guide, and still makes it clear that it can fail. > > The 'ForPrototype' part of the name is less important in my view, > > considering that there is an argument called prototype, it should be > > sufficiently clear. > I still don't like that name. I'm not sure what our style guideline says > here, but I think we tend to not choose names like this that would make a > grammatically incorrect sentence. > > > > > Also removed the comments that Saam finds obvious, and updated the year in > > the top-of-file copyright comments.
I guess if we followed the style guide to a T, we'd end up with: emptyObjectStructureIfExists. FWIW, "Concurrently" in JSC is sorta like "IfExists" in the rest of WebKit. I think we use "Concurrently" to generally mean "can return null".
Robin Morisset
Comment 16
2019-04-15 12:56:15 PDT
(In reply to Yusuke Suzuki from
comment #12
)
> Comment on
attachment 367361
[details]
> Patch > > View in context: >
https://bugs.webkit.org/attachment.cgi?id=367361&action=review
> > >> Source/JavaScriptCore/runtime/StructureCache.h:61 > >> StructureMap m_structures; > > > > This is WeakGCMap. So it is held by Heap, and collector checks this without taking this lock. > > I think it is safe since we don't run GC end phase while compiler thread is running. > > Another thing I would like to ensure is that this holds Weak<> handles. > > I think this does not return dead cells when the compiler threads successfully read it concurrently. > > Can you ensure they are safe? > > Sorry, I'll simplify the questions. Can you answer the following questions? > > 1. WeakGCMap is registered to the Heap, and GC repeatedly looks into it. But > at that time, collector does not take this m_lock. Is it safe?
The collector can read into the WeakGCMap whenever it wants, that won't cause trouble to the compiler thread that only reads it too. The collector only mutates it at the end of GC phase, when the compiler thread is paused between phases, so no race there either. The lock clearly protects against races between the mutator main thread and the compiler threads. So it only leaves mutator main thread/collector as a potential hazard. I am not sure how that one is safe, but since it was not changed in any way by my change, I don't see how I could have made it unsafe.
> 2. WeakGCMap holds Weak<>, and concurrent compiler reads cells from it. Do > concurrent compilers read the live cells or nullptr from this Weak<> handles?
I think (with low confidence) that it should read the live cells, as Heap::runEndPhase basically goes "pause all compiler threads -> reap weak handles -> prune weakGCMaps", so the compiler thread should never see a weakGCMap with reaped but not pruned Weak<>. The good news is that even if I am wrong on that point it should not matter: tryEmptyObjectStructureConcurrently is always allowed to return nullptr for any reason (since it does not try to build the structure if it is not already in the cache), and both of its clients check that the return is not null. Did this answer your questions?
Robin Morisset
Comment 17
2019-04-15 13:11:42 PDT
Created
attachment 367446
[details]
Patch New name: emptyObjectStructureConcurrently I was not aware that Concurrently canonically implies try/ifExists.
Saam Barati
Comment 18
2019-04-15 14:45:40 PDT
Comment on
attachment 367446
[details]
Patch View in context:
https://bugs.webkit.org/attachment.cgi?id=367446&action=review
> Source/JavaScriptCore/runtime/StructureCache.h:55 > + JS_EXPORT_PRIVATE Structure* tryEmptyObjectStructureConcurrently(JSGlobalObject*, JSObject* prototype, unsigned inlineCapacity);
still using the old name.
Saam Barati
Comment 19
2019-04-15 14:45:58 PDT
Comment on
attachment 367446
[details]
Patch View in context:
https://bugs.webkit.org/attachment.cgi?id=367446&action=review
> Source/JavaScriptCore/runtime/StructureCache.cpp:71 > + PrototypeKey key { prototype, nullptr, inlineCapacity, JSFinalObject::info(), globalObject};
nit: space before "}"
Saam Barati
Comment 20
2019-04-15 14:47:06 PDT
Comment on
attachment 367446
[details]
Patch View in context:
https://bugs.webkit.org/attachment.cgi?id=367446&action=review
> Source/JavaScriptCore/runtime/StructureCache.cpp:74 > + ASSERT(prototype->mayBePrototype());
this assertion is wrong. Nothing guaranteeing that this thing is already a prototype
Saam Barati
Comment 21
2019-04-15 14:47:35 PDT
Comment on
attachment 367446
[details]
Patch View in context:
https://bugs.webkit.org/attachment.cgi?id=367446&action=review
>> Source/JavaScriptCore/runtime/StructureCache.cpp:74 >> + ASSERT(prototype->mayBePrototype()); > > this assertion is wrong. Nothing guaranteeing that this thing is already a prototype
oops. Ignore me! It's in the map, so of course it's already a prototype!
Saam Barati
Comment 22
2019-04-15 15:17:23 PDT
***
Bug 186199
has been marked as a duplicate of this bug. ***
Robin Morisset
Comment 23
2019-04-15 16:22:20 PDT
(In reply to Saam Barati from
comment #18
)
> Comment on
attachment 367446
[details]
> Patch > > View in context: >
https://bugs.webkit.org/attachment.cgi?id=367446&action=review
> > > Source/JavaScriptCore/runtime/StructureCache.h:55 > > + JS_EXPORT_PRIVATE Structure* tryEmptyObjectStructureConcurrently(JSGlobalObject*, JSObject* prototype, unsigned inlineCapacity); > > still using the old name.
oh?! I must have updated the wrong patch. Fixing it now.
Robin Morisset
Comment 24
2019-04-15 16:22:50 PDT
Created
attachment 367475
[details]
PatchForLanding
WebKit Commit Bot
Comment 25
2019-04-15 17:28:53 PDT
Comment on
attachment 367475
[details]
PatchForLanding Clearing flags on attachment: 367475 Committed
r244313
: <
https://trac.webkit.org/changeset/244313
>
WebKit Commit Bot
Comment 26
2019-04-15 17:28:55 PDT
All reviewed patches have been landed. Closing bug.
Radar WebKit Bug Importer
Comment 27
2019-04-15 17:29:19 PDT
<
rdar://problem/49924798
>
Note
You need to
log in
before you can comment on or make changes to this bug.
Top of Page
Format For Printing
XML
Clone This Bug