RESOLVED FIXED 172216
[FTL] Support GetByVal with ArrayStorage and SlowPutArrayStorage
https://bugs.webkit.org/show_bug.cgi?id=172216
Summary [FTL] Support GetByVal with ArrayStorage and SlowPutArrayStorage
Yusuke Suzuki
Reported 2017-05-17 05:32:44 PDT
Let's do this to 1. pave the way to handle mandelbrot.js in FTL 2. support ArrayStorage / SlowPutArrayStorage arrays, like, tagged template call site object (SixSpeed)
Attachments
Patch (15.93 KB, patch)
2017-05-17 05:34 PDT, Yusuke Suzuki
no flags
Patch (36.13 KB, patch)
2017-05-17 07:36 PDT, Yusuke Suzuki
no flags
Patch (37.42 KB, patch)
2017-05-17 07:47 PDT, Yusuke Suzuki
no flags
Archive of layout-test-results from ews101 for mac-elcapitan (1.09 MB, application/zip)
2017-05-17 12:01 PDT, Build Bot
no flags
Patch (40.95 KB, patch)
2017-05-20 05:57 PDT, Yusuke Suzuki
saam: review+
Yusuke Suzuki
Comment 1 2017-05-17 05:34:31 PDT
Created attachment 310383 [details] Patch WIP: it works! Need tests
Build Bot
Comment 2 2017-05-17 06:14:32 PDT
Comment on attachment 310383 [details] Patch Attachment 310383 [details] did not pass jsc-ews (mac): Output: http://webkit-queues.webkit.org/results/3762326 New failing tests: jsc-layout-tests.yaml/js/script-tests/array-iterate-backwards.js.layout-ftl-eager-no-cjit
Yusuke Suzuki
Comment 3 2017-05-17 07:36:27 PDT
Created attachment 310388 [details] Patch WIP
Yusuke Suzuki
Comment 4 2017-05-17 07:47:54 PDT
Created attachment 310390 [details] Patch WIP
Build Bot
Comment 5 2017-05-17 12:01:55 PDT
Comment on attachment 310390 [details] Patch Attachment 310390 [details] did not pass mac-ews (mac): Output: http://webkit-queues.webkit.org/results/3764007 New failing tests: http/tests/media/hls/video-controls-live-stream.html
Build Bot
Comment 6 2017-05-17 12:01:56 PDT
Created attachment 310422 [details] Archive of layout-test-results from ews101 for mac-elcapitan The attached test failures were seen while running run-webkit-tests on the mac-ews. Bot: ews101 Port: mac-elcapitan Platform: Mac OS X 10.11.6
Yusuke Suzuki
Comment 7 2017-05-20 05:57:26 PDT
Saam Barati
Comment 8 2017-05-21 13:36:23 PDT
Comment on attachment 310768 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=310768&action=review LGTM, just want clarification on your use of a particular operation. > Source/JavaScriptCore/ChangeLog:12 > + This patch adds GetByVal support for ArrayStorage and SlowPutArrayStorage. > + To lower CheckInBounds in FTL, we add a new GetVectorLength op. It only accepts > + ArrayStorage and SlowPutArrayStorage, then it produces vector length. > + CheckInBounds uses this vector length to perform bound checking for ArrayStorage > + and SlowPutArrayStorage. Do we need something like this for the DFG as well, or is that already done? > Source/JavaScriptCore/dfg/DFGClobberize.h:1143 > + case GetVectorLength: { You should add a validation rule for GetVectorLength > Source/JavaScriptCore/ftl/FTLCapabilities.cpp:351 > + default: > + return CannotCompile; Isn't this invalid form? > Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp:3601 > + vmCall(Int64, m_out.operation(operationGetByValArrayInt), m_callFrame, base, index)); How do we know the base is an array? What if it's just a normal object w/ this kind of indexing type? Maybe this bug already exists in the FTL today?
Saam Barati
Comment 9 2017-05-21 13:47:07 PDT
Maybe we already have a bug. This program: ``` function foo(a, i) { return a[i]; } noInline(foo); let array = {}; array[0] = 10; array[1] = 20; array.foo = 20; for (let i = 0; i < 10000; ++i) { foo(array, (Math.random() * 3) | 0); } ``` Produces this FTL code for `foo`: ``` 19:<!0:-> ExitOK(MustGen, W:SideState, bc#0) 23:< 3:-> GetStack(JS|PureInt, Final, arg1, machine:arg1, FlushedCell, R:Stack(6), bc#0) 24:<!0:-> CheckArray(Cell:@23, MustGen, Int32+NonArray+OutOfBounds+AsIs, R:JSCell_indexingType,JSCell_structureID,JSCell_typeInfoType, Exits, bc#0) 21:< 1:-> GetStack(JS|PureInt, BoolInt32, arg2, machine:arg2, FlushedInt32, R:Stack(7), bc#0) 15:<!0:-> KillStack(MustGen, loc0, W:SideState, ClobbersExit, bc#0) 4:<!0:-> ZombieHint(MustGen, loc0, W:SideState, ClobbersExit, bc#0, ExitInvalid) 13:<!0:-> KillStack(MustGen, loc1, W:SideState, ClobbersExit, bc#0, ExitInvalid) 6:<!0:-> ZombieHint(MustGen, loc1, W:SideState, ClobbersExit, bc#0, ExitInvalid) 11:<!0:-> KillStack(MustGen, loc2, W:SideState, ClobbersExit, bc#0, ExitInvalid) 8:<!0:-> ZombieHint(MustGen, loc2, W:SideState, ClobbersExit, bc#0, ExitInvalid) 9:<!0:-> KillStack(MustGen, loc3, W:SideState, ClobbersExit, bc#0, ExitInvalid) 10:<!0:-> ZombieHint(MustGen, loc3, W:SideState, ClobbersExit, bc#0, ExitInvalid) 7:<!0:-> KillStack(MustGen, loc4, W:SideState, ClobbersExit, bc#0, ExitInvalid) 12:<!0:-> ZombieHint(MustGen, loc4, W:SideState, ClobbersExit, bc#0, ExitInvalid) 5:<!0:-> KillStack(MustGen, loc5, W:SideState, ClobbersExit, bc#0, ExitInvalid) 14:<!0:-> ZombieHint(MustGen, loc5, W:SideState, ClobbersExit, bc#0, ExitInvalid) 2:<!0:-> KillStack(MustGen, loc3, W:SideState, ClobbersExit, bc#1) 18:<!0:-> ZombieHint(MustGen, loc3, W:SideState, ClobbersExit, bc#1, ExitInvalid) 1:<!0:-> KillStack(MustGen, loc4, W:SideState, ClobbersExit, bc#3) 20:<!0:-> ZombieHint(MustGen, loc4, W:SideState, ClobbersExit, bc#3, ExitInvalid) 22:<!0:-> CheckTraps(MustGen, W:Watchpoint_fire, Exits, ClobbersExit, bc#6) 29:<!0:-> InvalidationPoint(MustGen, W:SideState, Exits, bc#7) 30:< 1:-> GetButterfly(Cell:@23, Storage|PureInt, R:JSObject_butterfly, Exits, bc#7) 25:<!2:-> GetByVal(KnownCell:Kill:@23, Int32:Kill:@21, Untyped:Kill:@30, JS|MustGen|UseAsOther, Int32, Int32+NonArray+OutOfBounds+AsIs, R:World, W:Heap, Exits, ClobbersExit, bc#7) predicting NonBoolInt32 0:<!0:-> KillStack(MustGen, loc6, W:SideState, ClobbersExit, bc#7, ExitInvalid) 26:<!0:-> MovHint(Untyped:@25, MustGen, loc6, W:SideState, ClobbersExit, bc#7, ExitInvalid) 27:<!0:-> InvalidationPoint(MustGen, W:SideState, Exits, bc#7, exit: bc#13) 28:<!0:-> Return(Untyped:Kill:@25, MustGen, W:SideState, Exits, bc#13) ``` Digging into the CheckArray code, I don't think it guarantees the speculation fails if the thing is not an array. It's more like check indexing shape. My worry i the GetByVal code calls operationGetByValArrayInt even when we haven't proven it's an array. Let me verify this actually happens, I'm building now.
Saam Barati
Comment 10 2017-05-21 14:03:49 PDT
(In reply to Saam Barati from comment #9) > Maybe we already have a bug. This program: > > ``` > function foo(a, i) { > return a[i]; > } > noInline(foo); > > let array = {}; > array[0] = 10; > array[1] = 20; > array.foo = 20; > > for (let i = 0; i < 10000; ++i) { > foo(array, (Math.random() * 3) | 0); > } > ``` > > Produces this FTL code for `foo`: > ``` > 19:<!0:-> ExitOK(MustGen, W:SideState, bc#0) > 23:< 3:-> GetStack(JS|PureInt, Final, arg1, machine:arg1, FlushedCell, > R:Stack(6), bc#0) > 24:<!0:-> CheckArray(Cell:@23, MustGen, Int32+NonArray+OutOfBounds+AsIs, > R:JSCell_indexingType,JSCell_structureID,JSCell_typeInfoType, Exits, bc#0) > 21:< 1:-> GetStack(JS|PureInt, BoolInt32, arg2, machine:arg2, > FlushedInt32, R:Stack(7), bc#0) > 15:<!0:-> KillStack(MustGen, loc0, W:SideState, ClobbersExit, bc#0) > 4:<!0:-> ZombieHint(MustGen, loc0, W:SideState, ClobbersExit, bc#0, > ExitInvalid) > 13:<!0:-> KillStack(MustGen, loc1, W:SideState, ClobbersExit, bc#0, > ExitInvalid) > 6:<!0:-> ZombieHint(MustGen, loc1, W:SideState, ClobbersExit, bc#0, > ExitInvalid) > 11:<!0:-> KillStack(MustGen, loc2, W:SideState, ClobbersExit, bc#0, > ExitInvalid) > 8:<!0:-> ZombieHint(MustGen, loc2, W:SideState, ClobbersExit, bc#0, > ExitInvalid) > 9:<!0:-> KillStack(MustGen, loc3, W:SideState, ClobbersExit, bc#0, > ExitInvalid) > 10:<!0:-> ZombieHint(MustGen, loc3, W:SideState, ClobbersExit, bc#0, > ExitInvalid) > 7:<!0:-> KillStack(MustGen, loc4, W:SideState, ClobbersExit, bc#0, > ExitInvalid) > 12:<!0:-> ZombieHint(MustGen, loc4, W:SideState, ClobbersExit, bc#0, > ExitInvalid) > 5:<!0:-> KillStack(MustGen, loc5, W:SideState, ClobbersExit, bc#0, > ExitInvalid) > 14:<!0:-> ZombieHint(MustGen, loc5, W:SideState, ClobbersExit, bc#0, > ExitInvalid) > 2:<!0:-> KillStack(MustGen, loc3, W:SideState, ClobbersExit, bc#1) > 18:<!0:-> ZombieHint(MustGen, loc3, W:SideState, ClobbersExit, bc#1, > ExitInvalid) > 1:<!0:-> KillStack(MustGen, loc4, W:SideState, ClobbersExit, bc#3) > 20:<!0:-> ZombieHint(MustGen, loc4, W:SideState, ClobbersExit, bc#3, > ExitInvalid) > 22:<!0:-> CheckTraps(MustGen, W:Watchpoint_fire, Exits, ClobbersExit, bc#6) > 29:<!0:-> InvalidationPoint(MustGen, W:SideState, Exits, bc#7) > 30:< 1:-> GetButterfly(Cell:@23, Storage|PureInt, R:JSObject_butterfly, > Exits, bc#7) > 25:<!2:-> GetByVal(KnownCell:Kill:@23, Int32:Kill:@21, Untyped:Kill:@30, > JS|MustGen|UseAsOther, Int32, Int32+NonArray+OutOfBounds+AsIs, R:World, > W:Heap, Exits, ClobbersExit, bc#7) predicting NonBoolInt32 > 0:<!0:-> KillStack(MustGen, loc6, W:SideState, ClobbersExit, bc#7, > ExitInvalid) > 26:<!0:-> MovHint(Untyped:@25, MustGen, loc6, W:SideState, ClobbersExit, > bc#7, ExitInvalid) > 27:<!0:-> InvalidationPoint(MustGen, W:SideState, Exits, bc#7, exit: bc#13) > 28:<!0:-> Return(Untyped:Kill:@25, MustGen, W:SideState, Exits, bc#13) > ``` > > Digging into the CheckArray code, I don't think it guarantees the > speculation fails if the thing is not an array. It's more like check > indexing shape. My worry i the GetByVal code calls operationGetByValArrayInt > even when we haven't proven it's an array. Let me verify this actually > happens, I'm building now. Yeah we totally call this function here. Anyways, this is a benign bug, since it immediately lowers the type to cell: ``` ALWAYS_INLINE EncodedJSValue getByValCellInt(ExecState* exec, JSCell* base, int32_t index) { VM* vm = &exec->vm(); NativeCallFrameTracer tracer(vm, exec); if (index < 0) { // Go the slowest way possible becase negative indices don't use indexed storage. return JSValue::encode(JSValue(base).get(exec, Identifier::from(exec, index))); } // Use this since we know that the value is out of bounds. return JSValue::encode(JSValue(base).get(exec, static_cast<unsigned>(index))); } EncodedJSValue JIT_OPERATION operationGetByValArrayInt(ExecState* exec, JSArray* base, int32_t index) { return getByValCellInt(exec, base, index); } ``` That said, lets please rename this function since it's clearly being misused. Perhaps we can just pass an object base.
Saam Barati
Comment 11 2017-05-21 14:04:25 PDT
r=me with function rename and type modification.
Yusuke Suzuki
Comment 12 2017-05-21 22:41:27 PDT
Comment on attachment 310768 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=310768&action=review Thanks. OK, I'll rename the function to operationGetByValObjectInt. >> Source/JavaScriptCore/ChangeLog:12 >> + and SlowPutArrayStorage. > > Do we need something like this for the DFG as well, or is that already done? Yeah, DFG already has this. FTL missed it, then this patch adds it.
Yusuke Suzuki
Comment 13 2017-05-21 23:22:42 PDT
Comment on attachment 310768 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=310768&action=review >> Source/JavaScriptCore/dfg/DFGClobberize.h:1143 >> + case GetVectorLength: { > > You should add a validation rule for GetVectorLength It's nice idea. Added. >> Source/JavaScriptCore/ftl/FTLCapabilities.cpp:351 >> + return CannotCompile; > > Isn't this invalid form? Yes, I've changed it to RELEASE_ASSERT_NOT_REACHED().
Yusuke Suzuki
Comment 14 2017-05-21 23:24:52 PDT
Note You need to log in before you can comment on or make changes to this bug.