Bug 145243

Summary: JIT bug - fails when inspector closed, works when open
Product: WebKit Reporter: Jordan Harband <ljharb>
Component: JavaScriptCoreAssignee: Nobody <webkit-unassigned>
Status: RESOLVED FIXED    
Severity: Blocker CC: barraclough, fpizlo, ggaren, msaboff, oliver, webkit-bug-importer, ysuzuki
Priority: P1 Keywords: InRadar
Version: 528+ (Nightly build)   
Hardware: Unspecified   
OS: Unspecified   
URL: http://rawgit.com/es-shims/es6-shim/master/test/index.html
Attachments:
Description Flags
Patch oliver: review+

Description Jordan Harband 2015-05-20 21:52:18 PDT
When accessing http://rawgit.com/es-shims/es6-shim/master/test/index.html on a current Nightly revision, it will fail with a bunch of `obj.__flags` errors (something in mocha/chai). However, when the dev inspector is *open*, they all/mostly pass as expected (some failures in older revisions are fine).

This appears to have been introduced by r182007, r182008, or r182009 - http://trac.webkit.org/changeset/182009 looks related perhaps?

Obviously this is super critical, and since I have no idea how it's broken I have no idea how many things this could break across the web :-)
Comment 1 Jordan Harband 2015-05-20 21:53:59 PDT
(thanks to yusuke for confirming it's a JIT bug, and yusuke and twitter.com/rosskukulinski for helping me narrow down the problem revisions)
Comment 2 Yusuke Suzuki 2015-05-20 22:33:40 PDT
hm, at r182007, it already fails.
So there's issue before r182007.

The issue looks like that.
In chai.js, we call

function Assertion(...) {
    flag(this, '...', ...);
    flag(this, '...', ...);
    flag(this, '...', ...);
}

function flag(obj, ...) {
    accessing obj.__flags become error (obj is undefined)
}

function expect(...) {
    return new Assertion(...);
}

I guess
1. accidentally accessing incorrect location of arguments (`obj`).
2. accidentally `this` becomes undefined.
Comment 3 Jordan Harband 2015-05-20 23:18:53 PDT
Hm, then it must be between r181893 and r182007 ?
Comment 4 Yusuke Suzuki 2015-05-21 00:05:14 PDT
r181993 seems the cause of this failure.
In r181993, I've made sure that this issue occurs.
http://trac.webkit.org/changeset/181993
Comment 5 Radar WebKit Bug Importer 2015-05-21 12:07:11 PDT
<rdar://problem/21061293>
Comment 6 Michael Saboff 2015-06-15 13:38:37 PDT
The failure is manifest in the following code:

function Assertion (obj, msg, stack) {
    flag(this, 'ssfi', stack  || arguments.callee);  // Eliminate arguments.callee and the bug goes away
    flag(this, 'object', obj);   // The first argument, "this", is undefined in "flag()"
    flag(this, 'message', msg);
}

function flag(obj, key, value) {
    var flags = obj.__flags || (obj.__flags = Object.create(null));
    if (arguments.length === 3) {
        flags[key] = value;
    } else {
        return flags[key];
    }
}

The bytecode for Assertion is:
Assertion#BsmUKj:[0x10b7dc720->0x10a04ab00, NoneFunctionConstruct, 140]: 140 m_instructions; 1120 bytes; 4 parameter(s); 14 callee register(s); 3 variable(s)
[   0] enter
[   1] get_scope         loc0
[   3] create_direct_arguments loc1
[   5] create_this       this, this, 0, 0
[  10] mov               loc2, loc1
[  13] resolve_scope     loc8, loc0, flag(@id0), 1<ThrowIfNotFound|GlobalVar>, 0
[  20] get_from_scope    loc3, loc8, flag(@id0), 1<ThrowIfNotFound|GlobalVar>, 201294216    predicting None
[  28] mov               loc7, this
[  31] mov               loc6, String (atomic) (identifier): ssfi, ID: 4(const0)
[  34] get_from_arguments loc5, loc1, 2    predicting None
[  39] jtrue             loc5, 12(->51)
[  42] get_by_id         loc5, loc2, callee(@id1)    predicting None
[  51] call              loc3, loc3, 4, 14 status(Could Take Slow Path, maxNumArguments = 1)    Original; predicting None
[  60] resolve_scope     loc8, loc0, flag(@id0), 1<ThrowIfNotFound|GlobalVar>, 0
[  67] get_from_scope    loc3, loc8, flag(@id0), 1<ThrowIfNotFound|GlobalVar>, 201294216    predicting None
[  75] mov               loc7, this
[  78] mov               loc6, String (atomic) (identifier): object, ID: 4(const1)
[  81] get_from_arguments loc5, loc1, 0    predicting None
[  86] call              loc3, loc3, 4, 14 status(Could Take Slow Path, maxNumArguments = 1)    Original; predicting None
[  95] resolve_scope     loc8, loc0, flag(@id0), 1<ThrowIfNotFound|GlobalVar>, 0
[ 102] get_from_scope    loc3, loc8, flag(@id0), 1<ThrowIfNotFound|GlobalVar>, 201294216    predicting None
[ 110] mov               loc7, this
[ 113] mov               loc6, String (atomic) (identifier): message, ID: 4(const2)
[ 116] get_from_arguments loc5, loc1, 1    predicting None
[ 121] call              loc3, loc3, 4, 14 status(Could Take Slow Path, maxNumArguments = 1)    Original; predicting None
[ 130] is_object         loc3, this
[ 133] jtrue             loc3, 5(->138)
[ 136] ret               this
[ 138] ret               this

The bytecode for flag is:
flag#D14VaO:[0x10b7dc980->0x10a04aa00, NoneFunctionCall, 132]: 132 m_instructions; 1056 bytes; 4 parameter(s); 14 callee register(s); 4 variable(s)
[   0] enter
[   1] get_scope         loc0
[   3] create_direct_arguments loc1
[   5] mov               loc3, loc1
[   8] get_from_arguments loc5, loc1, 0    predicting None
[  13] get_by_id         loc4, loc5, __flags(@id0)    predicting None
[  22] jtrue             loc4, 56(->78)
[  25] get_from_arguments loc5, loc1, 0    predicting None
[  30] resolve_scope     loc8, loc0, Object(@id1), 0<ThrowIfNotFound|GlobalProperty>, 0
[  37] get_from_scope    loc8, loc8, Object(@id1), 0<ThrowIfNotFound|GlobalProperty>, 101    predicting None
[  45] get_by_id         loc6, loc8, create(@id2)    predicting None
[  54] mov               loc7, Null(const0)
[  57] call              loc6, loc6, 2, 14 status(Could Take Slow Path, maxNumArguments = 1)    Original; predicting None
[  66] put_by_id         loc5, __flags(@id0), loc6
[  75] mov               loc4, loc6
[  78] mov               loc2, loc4
[  81] get_by_id         loc4, loc3, length(@id3)    predicting None
[  90] stricteq          loc4, loc4, Int32: 3(const1)
[  94] jfalse            loc4, 23(->117)
[  97] mov               loc4, loc2
[ 100] get_from_arguments loc5, loc1, 1    predicting None
[ 105] get_from_arguments loc6, loc1, 2    predicting None
[ 110] put_by_val        loc4, loc5, loc6    Original
[ 115] jmp               15(->130)
[ 117] get_from_arguments loc4, loc1, 1    predicting None
[ 122] get_by_val        loc4, loc2, loc4    Original; predicting None
[ 128] ret               loc4
[ 130] ret               Undefined(const2)

All three instances of "flag()" are inlined.  The problem is during the second call to flag(), we get "undefined" for the base object when executing bc#13, get_by_id.  The reason is during local CSE, we replace object we use as the base.  Before local CSE:
   189:< 1:->   CreateDirectArguments(JS|UseAsOther, Directarguments, R:Stack,HeapObjectCount, W:HeapObjectCount, bc#3)
...
   194:< 1:->   GetFromArguments(Check:KnownCell:@189, JS|UseAsOther, Final, capturedArgument0, R:DirectArgumentsProperties(0), bc#8)  predicting Final
...
   197:<!0:->   GetById(Check:Cell:@194, JS|MustGen|UseAsOther, Final, id2{__flags}, R:World, W:Heap, bc#13)  predicting Final

After local CSE:
 171:<!0:->     GetLocal(@457, JS|MustGen|UseAsOther, Directarguments, loc1(I<DirectArguments>/FlushedCell), R:Stack(-2), bc#81)  predicting Directarguments
 172:< 1:->     GetFromArguments(Check:KnownCell:@171, JS|UseAsOther, Other, capturedArgument0, R:DirectArgumentsProperties(0), bc#81)  predicting Other
   197:<!0:->   GetById(Check:Cell:@172, JS|MustGen|UseAsOther, Final, id2{__flags}, R:World, W:Heap, bc#13)  predicting Final

Where 172 is the GetFromArguments in Assertion.

The problem is in DFGClobberize::cloberize() and its handling of GetFromArguments.
Comment 7 Michael Saboff 2015-06-15 14:09:54 PDT
Created attachment 254893 [details]
Patch
Comment 8 Michael Saboff 2015-06-15 14:26:44 PDT
Committed r185566: <http://trac.webkit.org/changeset/185566>
Comment 9 Filip Pizlo 2015-06-15 14:33:55 PDT
Comment on attachment 254893 [details]
Patch

r=me too
Comment 10 Jordan Harband 2015-06-16 22:47:42 PDT
Thanks, this looks great in the latest nightly build!