WebKit Bugzilla
New
Browse
Log In
×
Sign in with GitHub
or
Remember my login
Create Account
·
Forgot Password
Forgotten password account recovery
RESOLVED FIXED
180852
The CleanUp after LICM is erroneously removing a Check
https://bugs.webkit.org/show_bug.cgi?id=180852
Summary
The CleanUp after LICM is erroneously removing a Check
Mark Lam
Reported
2017-12-14 17:20:33 PST
Created
attachment 329425
[details]
Repro test case. This results in a subsequent node expecting a checked String object, but instead, got a non-String cell, which leads to a crash. I ran the test as follows: $ JSC_useConcurrentJIT=0 jsc repro1.js
Attachments
Repro test case.
(295 bytes, application/x-javascript)
2017-12-14 17:20 PST
,
Mark Lam
no flags
Details
patch
(6.44 KB, patch)
2017-12-14 19:03 PST
,
Saam Barati
fpizlo
: review-
Details
Formatted Diff
Diff
patch
(8.05 KB, patch)
2017-12-14 19:24 PST
,
Saam Barati
fpizlo
: review+
Details
Formatted Diff
Diff
patch for landing
(8.14 KB, patch)
2017-12-14 19:33 PST
,
Saam Barati
no flags
Details
Formatted Diff
Diff
patch for landing
(8.16 KB, patch)
2017-12-14 19:36 PST
,
Saam Barati
no flags
Details
Formatted Diff
Diff
Show Obsolete
(3)
View All
Add attachment
proposed patch, testcase, etc.
Radar WebKit Bug Importer
Comment 1
2017-12-14 17:21:28 PST
<
rdar://problem/36063494
>
Saam Barati
Comment 2
2017-12-14 18:06:25 PST
The bug is we're running part of AI inside LICM. This part of AI will change the proof status of a node. The bug here is: We have two loops, L1 and L2, and two preheaders, P1 and P2. L2 is nested inside of L1. We have a Check inside a node inside L1, say in block BB, and that Check dominates all of L2. This is also a hoisting candidate, so we hoist it outside of L1 and put it inside P1. Then, when we run AI, we look at the preheader for each loop inside L1, so P1 and P2. When considering P2, we execute the Check. Inside P2, before any hoisting is done, this Check is dead code, because BB dominates P2. When we use AI to "execute" the Check, it'll set its proof status to proved. This is because inside P2, in the program before LICM runs, the Check is indeed proven at P2. But it is not proven inside P1. This "execute" call will set our proof status for the node inside *P1*, hence, we crash. I see two easy solutions: 1. Remove the CleanUp phase after LICM 2. Run CFA before the CleanUP phase after LICM I vote for (1) since CFA is expensive to run.
Saam Barati
Comment 3
2017-12-14 18:15:14 PST
Yet another alternative solution: Clear isProved() for the children of all nodes we hoist.
Filip Pizlo
Comment 4
2017-12-14 18:30:15 PST
(In reply to Saam Barati from
comment #2
)
> The bug is we're running part of AI inside LICM.
How is that a bug? It should generate a valid proof.
> This part of AI will change > the proof status of a node. The bug here is: > > We have two loops, L1 and L2, and two preheaders, P1 and P2. L2 is nested > inside of L1. We have a Check inside a node inside L1, say in block BB, and > that Check dominates all of L2. This is also a hoisting candidate, so we > hoist it outside of L1 and put it inside P1. Then, when we run AI, we look > at the preheader for each loop inside L1, so P1 and P2. When considering P2, > we execute the Check. Inside P2, before any hoisting is done, this Check is > dead code, because BB dominates P2. When we use AI to "execute" the Check, > it'll set its proof status to proved. This is because inside P2, in the > program before LICM runs, the Check is indeed proven at P2. But it is not > proven inside P1. This "execute" call will set our proof status for the node > inside *P1*, hence, we crash. > > I see two easy solutions: > 1. Remove the CleanUp phase after LICM > 2. Run CFA before the CleanUP phase after LICM > > I vote for (1) since CFA is expensive to run.
Clean up is pretty useful. How about 3: make licm’s use of AI leave behind valid AI state.
Filip Pizlo
Comment 5
2017-12-14 18:33:04 PST
Seems like we need a way of executing a node without changing its proved bits. That execution in LICM is to update another block’s state summary so it shouldn’t be changing proved bots. Maybe you can hook that into the existing AtTail versus InPlace template magic.
Saam Barati
Comment 6
2017-12-14 18:36:13 PST
(In reply to Saam Barati from
comment #3
)
> Yet another alternative solution: > Clear isProved() for the children of all nodes we hoist.
I like this option the most. It also seems like Fil predicted it a few years ago:
https://bugs.webkit.org/show_bug.cgi?id=148544
I think it's ok to be conservative and just say anything we hoist no longer has valid proofs for its children. Also, it's important to note that the act of hoisting a node will never change the proof status from IsProved to NeedsCheck for anything inside the loop. It could make it go from NeedsCheck to IsProved though, but that's purely profit that we don't gain until we run AI again.
Saam Barati
Comment 7
2017-12-14 18:37:39 PST
(In reply to Filip Pizlo from
comment #5
)
> Seems like we need a way of executing a node without changing its proved > bits. That execution in LICM is to update another block’s state summary so > it shouldn’t be changing proved bots. > > Maybe you can hook that into the existing AtTail versus InPlace template > magic.
Right. Let's say I make this change to not update proved bits for AtTail. What worries me is this: What if hoisting the node changes its proved bits? I'm not sure if this is possible, but it seems like it'd be better to be conservative and say things aren't proved once hoisted.
Saam Barati
Comment 8
2017-12-14 18:50:45 PST
(In reply to Saam Barati from
comment #7
)
> (In reply to Filip Pizlo from
comment #5
) > > Seems like we need a way of executing a node without changing its proved > > bits. That execution in LICM is to update another block’s state summary so > > it shouldn’t be changing proved bots. > > > > Maybe you can hook that into the existing AtTail versus InPlace template > > magic. > > Right. Let's say I make this change to not update proved bits for AtTail. > What worries me is this: > What if hoisting the node changes its proved bits? > > I'm not sure if this is possible, but it seems like it'd be better to be > conservative and say things aren't proved once hoisted.
I actually think this is necessary. The fact that we update the Proved bits probably helps us in a bunch of scenarios where we have something like this: ``` bb#1: a: Foo: produces Heap TOP jump #2 bb#2 b: CantBeHoisted(Check:Cell:@a) c: CanBeHoisted(Cell:@a) some stuff to make a loop ``` if we hoist without updating the proved bits, we're going to be sad
Saam Barati
Comment 9
2017-12-14 18:54:09 PST
I still like the idea of making this method as part of AbstractState though so it's a nop for LICM
Filip Pizlo
Comment 10
2017-12-14 18:56:31 PST
(In reply to Saam Barati from
comment #9
)
> I still like the idea of making this method as part of AbstractState though > so it's a nop for LICM
I’m fine with your solution, but it seems like there are two different execution modes in LICM at-tail: - execute to update state of nested headers - execute to establish the right state at the tail of the header we hoisted to. So, you could be precise if you wanted to be.
Saam Barati
Comment 11
2017-12-14 19:03:13 PST
Created
attachment 329435
[details]
patch
Filip Pizlo
Comment 12
2017-12-14 19:07:19 PST
Comment on
attachment 329435
[details]
patch View in context:
https://bugs.webkit.org/attachment.cgi?id=329435&action=review
I think this could be simpler.
> Source/JavaScriptCore/dfg/DFGAtTailAbstractState.h:67 > + void setProofStatus(Edge&, ProofStatus) { }
What if this set it to NeedsCheck? Then you don’t need more data structures.
> Source/JavaScriptCore/dfg/DFGLICMPhase.cpp:313 > + m_hoistedNodes.add(node);
I don’t think you need this.
> Source/JavaScriptCore/dfg/DFGLICMPhase.cpp:354 > + HashSet<Node*> m_hoistedNodes;
And if you did need it, it should probably be a vector. Optimizing away duplicates isn’t going to help you.
Saam Barati
Comment 13
2017-12-14 19:08:40 PST
(In reply to Filip Pizlo from
comment #10
)
> (In reply to Saam Barati from
comment #9
) > > I still like the idea of making this method as part of AbstractState though > > so it's a nop for LICM > > I’m fine with your solution, but it seems like there are two different > execution modes in LICM at-tail: > > - execute to update state of nested headers > > - execute to establish the right state at the tail of the header we hoisted > to. > > So, you could be precise if you wanted to be.
I see. Yeah this makes sense. We could do it with a bit inside AtTail. The patch I uploaded is conservative and never does it, and says anything we hoist can't have proven children. I like the idea of being precise. I'll make a new patch
Saam Barati
Comment 14
2017-12-14 19:18:25 PST
***
Bug 148544
has been marked as a duplicate of this bug. ***
Saam Barati
Comment 15
2017-12-14 19:24:06 PST
Created
attachment 329442
[details]
patch
Filip Pizlo
Comment 16
2017-12-14 19:28:28 PST
Comment on
attachment 329442
[details]
patch Nice!
Saam Barati
Comment 17
2017-12-14 19:29:15 PST
Comment on
attachment 329442
[details]
patch View in context:
https://bugs.webkit.org/attachment.cgi?id=329442&action=review
> Source/JavaScriptCore/ChangeLog:25 > + The fix I'm going with is to conservatively say the proof status for the > + children of all nodes we hoist is NeedsCheck.
This no longer reflects what I'm actually doing. I'll update it to be accurate.
Saam Barati
Comment 18
2017-12-14 19:33:41 PST
Created
attachment 329445
[details]
patch for landing
Saam Barati
Comment 19
2017-12-14 19:36:42 PST
Created
attachment 329446
[details]
patch for landing Fix typo
WebKit Commit Bot
Comment 20
2017-12-14 22:20:11 PST
Comment on
attachment 329446
[details]
patch for landing Clearing flags on attachment: 329446 Committed
r225966
: <
https://trac.webkit.org/changeset/225966
>
WebKit Commit Bot
Comment 21
2017-12-14 22:20:13 PST
All reviewed patches have been landed. Closing bug.
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