WebKit Bugzilla
Attachment 340665 Details for
Bug 185652
: JSC should have InstanceOf inline caching
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
the patch
blah.patch (text/plain), 161.98 KB, created by
Filip Pizlo
on 2018-05-17 16:30:18 PDT
(
hide
)
Description:
the patch
Filename:
MIME Type:
Creator:
Filip Pizlo
Created:
2018-05-17 16:30:18 PDT
Size:
161.98 KB
patch
obsolete
>Index: JSTests/ChangeLog >=================================================================== >--- JSTests/ChangeLog (revision 231917) >+++ JSTests/ChangeLog (working copy) >@@ -1,3 +1,463 @@ >+2018-05-16 Filip Pizlo <fpizlo@apple.com> >+ >+ JSC should have InstanceOf inline caching >+ https://bugs.webkit.org/show_bug.cgi?id=185652 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ * microbenchmarks/instanceof-always-hit-one.js: Added. >+ (Foo): >+ * microbenchmarks/instanceof-always-hit-two.js: Added. >+ (Foo): >+ (Bar): >+ * microbenchmarks/instanceof-dynamic.js: Added. >+ (Foo0): >+ (Foo1): >+ (Foo2): >+ (Foo3): >+ (Foo4): >+ (Foo5): >+ (Foo6): >+ (Foo7): >+ (Foo8): >+ (Foo9): >+ (Foo10): >+ (Foo11): >+ (Foo12): >+ (Foo13): >+ (Foo14): >+ (Foo15): >+ (Foo16): >+ (Foo17): >+ (Foo18): >+ (Foo19): >+ (Foo20): >+ (Foo21): >+ (Foo22): >+ (Foo23): >+ (Foo24): >+ (Foo25): >+ (Foo26): >+ (Foo27): >+ (Foo28): >+ (Foo29): >+ (Foo30): >+ (Foo31): >+ (Foo32): >+ (Foo33): >+ (Foo34): >+ (Foo35): >+ (Foo36): >+ (Foo37): >+ (Foo38): >+ (Foo39): >+ (Foo40): >+ (Foo41): >+ (Foo42): >+ (Foo43): >+ (Foo44): >+ (Foo45): >+ (Foo46): >+ (Foo47): >+ (Foo48): >+ (Foo49): >+ (Foo50): >+ (Foo51): >+ (Foo52): >+ (Foo53): >+ (Foo54): >+ (Foo55): >+ (Foo56): >+ (Foo57): >+ (Foo58): >+ (Foo59): >+ (Foo60): >+ (Foo61): >+ (Foo62): >+ (Foo63): >+ (Foo64): >+ (Foo65): >+ (Foo66): >+ (Foo67): >+ (Foo68): >+ (Foo69): >+ (Foo70): >+ (Foo71): >+ (Foo72): >+ (Foo73): >+ (Foo74): >+ (Foo75): >+ (Foo76): >+ (Foo77): >+ (Foo78): >+ (Foo79): >+ (Foo80): >+ (Foo81): >+ (Foo82): >+ (Foo83): >+ (Foo84): >+ (Foo85): >+ (Foo86): >+ (Foo87): >+ (Foo88): >+ (Foo89): >+ (Foo90): >+ (Foo91): >+ (Foo92): >+ (Foo93): >+ (Foo94): >+ (Foo95): >+ (Foo96): >+ (Foo97): >+ (Foo98): >+ (Foo99): >+ * microbenchmarks/instanceof-sometimes-hit.js: Added. >+ (Foo): >+ (Bar): >+ * microbenchmarks/path-specialization-loop-unswitching-big.js: Added. >+ (foo): >+ * microbenchmarks/path-specialization-loop-unswitching-impossible.js: Added. >+ (bar): >+ (foo): >+ * microbenchmarks/path-specialization-loop-unswitching.js: Added. >+ (foo): >+ * microbenchmarks/path-specialization-multi-by-offset-smaller.js: Added. >+ (foo): >+ * microbenchmarks/path-specialization-multi-by-offset.js: Added. >+ (foo): >+ * stress/instanceof-dynamic-proxy-check-structure.js: Added. >+ (Foo0): >+ (Foo1): >+ (Foo2): >+ (Foo3): >+ (Foo4): >+ (Foo5): >+ (Foo6): >+ (Foo7): >+ (Foo8): >+ (Foo9): >+ (Foo10): >+ (Foo11): >+ (Foo12): >+ (Foo13): >+ (Foo14): >+ (Foo15): >+ (Foo16): >+ (Foo17): >+ (Foo18): >+ (Foo19): >+ (Foo20): >+ (Foo21): >+ (Foo22): >+ (Foo23): >+ (Foo24): >+ (Foo25): >+ (Foo26): >+ (Foo27): >+ (Foo28): >+ (Foo29): >+ (Foo30): >+ (Foo31): >+ (Foo32): >+ (Foo33): >+ (Foo34): >+ (Foo35): >+ (Foo36): >+ (Foo37): >+ (Foo38): >+ (Foo39): >+ (Foo40): >+ (Foo41): >+ (Foo42): >+ (Foo43): >+ (Foo44): >+ (Foo45): >+ (Foo46): >+ (Foo47): >+ (Foo48): >+ (Foo49): >+ (Foo50): >+ (Foo51): >+ (Foo52): >+ (Foo53): >+ (Foo54): >+ (Foo55): >+ (Foo56): >+ (Foo57): >+ (Foo58): >+ (Foo59): >+ (Foo60): >+ (Foo61): >+ (Foo62): >+ (Foo63): >+ (Foo64): >+ (Foo65): >+ (Foo66): >+ (Foo67): >+ (Foo68): >+ (Foo69): >+ (Foo70): >+ (Foo71): >+ (Foo72): >+ (Foo73): >+ (Foo74): >+ (Foo75): >+ (Foo76): >+ (Foo77): >+ (Foo78): >+ (Foo79): >+ (Foo80): >+ (Foo81): >+ (Foo82): >+ (Foo83): >+ (Foo84): >+ (Foo85): >+ (Foo86): >+ (Foo87): >+ (Foo88): >+ (Foo89): >+ (Foo90): >+ (Foo91): >+ (Foo92): >+ (Foo93): >+ (Foo94): >+ (Foo95): >+ (Foo96): >+ (Foo97): >+ (Foo98): >+ (Foo99): >+ (Foo): >+ (Bar): >+ (doBadThings): >+ (getPrototypeOf): >+ (foo): >+ (new.Bar): >+ * stress/instanceof-dynamic-proxy-loop.js: Added. >+ (Foo0): >+ (Foo1): >+ (Foo2): >+ (Foo3): >+ (Foo4): >+ (Foo5): >+ (Foo6): >+ (Foo7): >+ (Foo8): >+ (Foo9): >+ (Foo10): >+ (Foo11): >+ (Foo12): >+ (Foo13): >+ (Foo14): >+ (Foo15): >+ (Foo16): >+ (Foo17): >+ (Foo18): >+ (Foo19): >+ (Foo20): >+ (Foo21): >+ (Foo22): >+ (Foo23): >+ (Foo24): >+ (Foo25): >+ (Foo26): >+ (Foo27): >+ (Foo28): >+ (Foo29): >+ (Foo30): >+ (Foo31): >+ (Foo32): >+ (Foo33): >+ (Foo34): >+ (Foo35): >+ (Foo36): >+ (Foo37): >+ (Foo38): >+ (Foo39): >+ (Foo40): >+ (Foo41): >+ (Foo42): >+ (Foo43): >+ (Foo44): >+ (Foo45): >+ (Foo46): >+ (Foo47): >+ (Foo48): >+ (Foo49): >+ (Foo50): >+ (Foo51): >+ (Foo52): >+ (Foo53): >+ (Foo54): >+ (Foo55): >+ (Foo56): >+ (Foo57): >+ (Foo58): >+ (Foo59): >+ (Foo60): >+ (Foo61): >+ (Foo62): >+ (Foo63): >+ (Foo64): >+ (Foo65): >+ (Foo66): >+ (Foo67): >+ (Foo68): >+ (Foo69): >+ (Foo70): >+ (Foo71): >+ (Foo72): >+ (Foo73): >+ (Foo74): >+ (Foo75): >+ (Foo76): >+ (Foo77): >+ (Foo78): >+ (Foo79): >+ (Foo80): >+ (Foo81): >+ (Foo82): >+ (Foo83): >+ (Foo84): >+ (Foo85): >+ (Foo86): >+ (Foo87): >+ (Foo88): >+ (Foo89): >+ (Foo90): >+ (Foo91): >+ (Foo92): >+ (Foo93): >+ (Foo94): >+ (Foo95): >+ (Foo96): >+ (Foo97): >+ (Foo98): >+ (Foo99): >+ (Foo): >+ (Bar): >+ (doBadThings): >+ (getPrototypeOf): >+ (foo): >+ * stress/instanceof-dynamic-proxy.js: Added. >+ (Foo0): >+ (Foo1): >+ (Foo2): >+ (Foo3): >+ (Foo4): >+ (Foo5): >+ (Foo6): >+ (Foo7): >+ (Foo8): >+ (Foo9): >+ (Foo10): >+ (Foo11): >+ (Foo12): >+ (Foo13): >+ (Foo14): >+ (Foo15): >+ (Foo16): >+ (Foo17): >+ (Foo18): >+ (Foo19): >+ (Foo20): >+ (Foo21): >+ (Foo22): >+ (Foo23): >+ (Foo24): >+ (Foo25): >+ (Foo26): >+ (Foo27): >+ (Foo28): >+ (Foo29): >+ (Foo30): >+ (Foo31): >+ (Foo32): >+ (Foo33): >+ (Foo34): >+ (Foo35): >+ (Foo36): >+ (Foo37): >+ (Foo38): >+ (Foo39): >+ (Foo40): >+ (Foo41): >+ (Foo42): >+ (Foo43): >+ (Foo44): >+ (Foo45): >+ (Foo46): >+ (Foo47): >+ (Foo48): >+ (Foo49): >+ (Foo50): >+ (Foo51): >+ (Foo52): >+ (Foo53): >+ (Foo54): >+ (Foo55): >+ (Foo56): >+ (Foo57): >+ (Foo58): >+ (Foo59): >+ (Foo60): >+ (Foo61): >+ (Foo62): >+ (Foo63): >+ (Foo64): >+ (Foo65): >+ (Foo66): >+ (Foo67): >+ (Foo68): >+ (Foo69): >+ (Foo70): >+ (Foo71): >+ (Foo72): >+ (Foo73): >+ (Foo74): >+ (Foo75): >+ (Foo76): >+ (Foo77): >+ (Foo78): >+ (Foo79): >+ (Foo80): >+ (Foo81): >+ (Foo82): >+ (Foo83): >+ (Foo84): >+ (Foo85): >+ (Foo86): >+ (Foo87): >+ (Foo88): >+ (Foo89): >+ (Foo90): >+ (Foo91): >+ (Foo92): >+ (Foo93): >+ (Foo94): >+ (Foo95): >+ (Foo96): >+ (Foo97): >+ (Foo98): >+ (Foo99): >+ (Foo): >+ (Bar): >+ (doBadThings): >+ (getPrototypeOf): >+ (foo): >+ * stress/instanceof-hit-one-object-then-another.js: Added. >+ (foo): >+ (Foo): >+ (Bar): >+ * stress/instanceof-hit-two-objects-then-another.js: Added. >+ (foo): >+ (Foo): >+ (Bar): >+ (Baz): >+ * stress/instanceof-prototype-change.js: Added. >+ (foo): >+ (Foo): >+ (Bar): >+ > 2018-05-16 Caio Lima <ticaiolima@gmail.com> > > [ESNext][BigInt] Implement support for "/" operation >Index: JSTests/microbenchmarks/instanceof-always-hit-one.js >=================================================================== >--- JSTests/microbenchmarks/instanceof-always-hit-one.js (nonexistent) >+++ JSTests/microbenchmarks/instanceof-always-hit-one.js (working copy) >@@ -0,0 +1,9 @@ >+(function() { >+ class Foo { } >+ var foo = new Foo(); >+ >+ for (var i = 0; i < 1000000; ++i) { >+ if (!(foo instanceof Foo)) >+ throw "Error: bad result"; >+ } >+})(); >Index: JSTests/microbenchmarks/instanceof-always-hit-two.js >=================================================================== >--- JSTests/microbenchmarks/instanceof-always-hit-two.js (nonexistent) >+++ JSTests/microbenchmarks/instanceof-always-hit-two.js (working copy) >@@ -0,0 +1,17 @@ >+(function() { >+ class Foo { } >+ var foo = new Foo(); >+ >+ class Bar extends Foo { } >+ var bar = new Bar(); >+ >+ for (var i = 0; i < 1000000; ++i) { >+ var o; >+ if (i & 1) >+ o = foo; >+ else >+ o = bar; >+ if (!(o instanceof Foo)) >+ throw "Error: bad result"; >+ } >+})(); >Index: JSTests/microbenchmarks/instanceof-dynamic.js >=================================================================== >--- JSTests/microbenchmarks/instanceof-dynamic.js (nonexistent) >+++ JSTests/microbenchmarks/instanceof-dynamic.js (working copy) >@@ -0,0 +1,115 @@ >+(function() { >+ class Foo0 { } >+ class Foo1 { } >+ class Foo2 { } >+ class Foo3 { } >+ class Foo4 { } >+ class Foo5 { } >+ class Foo6 { } >+ class Foo7 { } >+ class Foo8 { } >+ class Foo9 { } >+ class Foo10 { } >+ class Foo11 { } >+ class Foo12 { } >+ class Foo13 { } >+ class Foo14 { } >+ class Foo15 { } >+ class Foo16 { } >+ class Foo17 { } >+ class Foo18 { } >+ class Foo19 { } >+ class Foo20 { } >+ class Foo21 { } >+ class Foo22 { } >+ class Foo23 { } >+ class Foo24 { } >+ class Foo25 { } >+ class Foo26 { } >+ class Foo27 { } >+ class Foo28 { } >+ class Foo29 { } >+ class Foo30 { } >+ class Foo31 { } >+ class Foo32 { } >+ class Foo33 { } >+ class Foo34 { } >+ class Foo35 { } >+ class Foo36 { } >+ class Foo37 { } >+ class Foo38 { } >+ class Foo39 { } >+ class Foo40 { } >+ class Foo41 { } >+ class Foo42 { } >+ class Foo43 { } >+ class Foo44 { } >+ class Foo45 { } >+ class Foo46 { } >+ class Foo47 { } >+ class Foo48 { } >+ class Foo49 { } >+ class Foo50 { } >+ class Foo51 { } >+ class Foo52 { } >+ class Foo53 { } >+ class Foo54 { } >+ class Foo55 { } >+ class Foo56 { } >+ class Foo57 { } >+ class Foo58 { } >+ class Foo59 { } >+ class Foo60 { } >+ class Foo61 { } >+ class Foo62 { } >+ class Foo63 { } >+ class Foo64 { } >+ class Foo65 { } >+ class Foo66 { } >+ class Foo67 { } >+ class Foo68 { } >+ class Foo69 { } >+ class Foo70 { } >+ class Foo71 { } >+ class Foo72 { } >+ class Foo73 { } >+ class Foo74 { } >+ class Foo75 { } >+ class Foo76 { } >+ class Foo77 { } >+ class Foo78 { } >+ class Foo79 { } >+ class Foo80 { } >+ class Foo81 { } >+ class Foo82 { } >+ class Foo83 { } >+ class Foo84 { } >+ class Foo85 { } >+ class Foo86 { } >+ class Foo87 { } >+ class Foo88 { } >+ class Foo89 { } >+ class Foo90 { } >+ class Foo91 { } >+ class Foo92 { } >+ class Foo93 { } >+ class Foo94 { } >+ class Foo95 { } >+ class Foo96 { } >+ class Foo97 { } >+ class Foo98 { } >+ class Foo99 { } >+ >+ var foos = [new Foo0(), new Foo1(), new Foo2(), new Foo3(), new Foo4(), new Foo5(), new Foo6(), new Foo7(), new Foo8(), new Foo9(), new Foo10(), new Foo11(), new Foo12(), new Foo13(), new Foo14(), new Foo15(), new Foo16(), new Foo17(), new Foo18(), new Foo19(), new Foo20(), new Foo21(), new Foo22(), new Foo23(), new Foo24(), new Foo25(), new Foo26(), new Foo27(), new Foo28(), new Foo29(), new Foo30(), new Foo31(), new Foo32(), new Foo33(), new Foo34(), new Foo35(), new Foo36(), new Foo37(), new Foo38(), new Foo39(), new Foo40(), new Foo41(), new Foo42(), new Foo43(), new Foo44(), new Foo45(), new Foo46(), new Foo47(), new Foo48(), new Foo49(), new Foo50(), new Foo51(), new Foo52(), new Foo53(), new Foo54(), new Foo55(), new Foo56(), new Foo57(), new Foo58(), new Foo59(), new Foo60(), new Foo61(), new Foo62(), new Foo63(), new Foo64(), new Foo65(), new Foo66(), new Foo67(), new Foo68(), new Foo69(), new Foo70(), new Foo71(), new Foo72(), new Foo73(), new Foo74(), new Foo75(), new Foo76(), new Foo77(), new Foo78(), new Foo79(), new Foo80(), new Foo81(), new Foo82(), new Foo83(), new Foo84(), new Foo85(), new Foo86(), new Foo87(), new Foo88(), new Foo89(), new Foo90(), new Foo91(), new Foo92(), new Foo93(), new Foo94(), new Foo95(), new Foo96(), new Foo97(), new Foo98(), new Foo99()]; >+ >+ var fooIndex = 0; >+ for (var i = 0; i < 100000; ++i) { >+ var o = foos[fooIndex]; >+ var result = o instanceof Foo42; >+ if (result != (fooIndex == 42)) >+ throw "Error: bad result at i = " + i + ": " + result; >+ fooIndex++; >+ if (fooIndex >= 100) >+ fooIndex = 0; >+ } >+})(); >Index: JSTests/microbenchmarks/instanceof-sometimes-hit.js >=================================================================== >--- JSTests/microbenchmarks/instanceof-sometimes-hit.js (nonexistent) >+++ JSTests/microbenchmarks/instanceof-sometimes-hit.js (working copy) >@@ -0,0 +1,17 @@ >+(function() { >+ class Foo { } >+ var foo = new Foo(); >+ >+ class Bar { } >+ var bar = new Bar(); >+ >+ for (var i = 0; i < 100000; ++i) { >+ var o; >+ if (i & 1) >+ o = foo; >+ else >+ o = bar; >+ if ((o instanceof Foo) != !!(i & 1)) >+ throw "Error: bad result at i = " + i; >+ } >+})(); >Index: JSTests/microbenchmarks/instanceof-tricky-dynamic.js >=================================================================== >--- JSTests/microbenchmarks/instanceof-tricky-dynamic.js (nonexistent) >+++ JSTests/microbenchmarks/instanceof-tricky-dynamic.js (working copy) >@@ -0,0 +1,117 @@ >+(function() { >+ class Foo0 { } >+ class Foo1 { } >+ class Foo2 { } >+ class Foo3 { } >+ class Foo4 { } >+ class Foo5 { } >+ class Foo6 { } >+ class Foo7 { } >+ class Foo8 { } >+ class Foo9 { } >+ class Foo10 { } >+ class Foo11 { } >+ class Foo12 { } >+ class Foo13 { } >+ class Foo14 { } >+ class Foo15 { } >+ class Foo16 { } >+ class Foo17 { } >+ class Foo18 { } >+ class Foo19 { } >+ class Foo20 { } >+ class Foo21 { } >+ class Foo22 { } >+ class Foo23 { } >+ class Foo24 { } >+ class Foo25 { } >+ class Foo26 { } >+ class Foo27 { } >+ class Foo28 { } >+ class Foo29 { } >+ class Foo30 { } >+ class Foo31 { } >+ class Foo32 { } >+ class Foo33 { } >+ class Foo34 { } >+ class Foo35 { } >+ class Foo36 { } >+ class Foo37 { } >+ class Foo38 { } >+ class Foo39 { } >+ class Foo40 { } >+ class Foo41 { } >+ class Foo42 { } >+ class Foo43 { } >+ class Foo44 { } >+ class Foo45 { } >+ class Foo46 { } >+ class Foo47 { } >+ class Foo48 { } >+ class Foo49 { } >+ class Foo50 { } >+ class Foo51 { } >+ class Foo52 { } >+ class Foo53 { } >+ class Foo54 { } >+ class Foo55 { } >+ class Foo56 { } >+ class Foo57 { } >+ class Foo58 { } >+ class Foo59 { } >+ class Foo60 { } >+ class Foo61 { } >+ class Foo62 { } >+ class Foo63 { } >+ class Foo64 { } >+ class Foo65 { } >+ class Foo66 { } >+ class Foo67 { } >+ class Foo68 { } >+ class Foo69 { } >+ class Foo70 { } >+ class Foo71 { } >+ class Foo72 { } >+ class Foo73 { } >+ class Foo74 { } >+ class Foo75 { } >+ class Foo76 { } >+ class Foo77 { } >+ class Foo78 { } >+ class Foo79 { } >+ class Foo80 { } >+ class Foo81 { } >+ class Foo82 { } >+ class Foo83 { } >+ class Foo84 { } >+ class Foo85 { } >+ class Foo86 { } >+ class Foo87 { } >+ class Foo88 { } >+ class Foo89 { } >+ class Foo90 { } >+ class Foo91 { } >+ class Foo92 { } >+ class Foo93 { } >+ class Foo94 { } >+ class Foo95 { } >+ class Foo96 { } >+ class Foo97 { } >+ class Foo98 { } >+ class Foo99 { } >+ >+ var foos = [new Foo0(), new Foo1(), new Foo2(), new Foo3(), new Foo4(), new Foo5(), new Foo6(), new Foo7(), new Foo8(), new Foo9(), new Foo10(), new Foo11(), new Foo12(), new Foo13(), new Foo14(), new Foo15(), new Foo16(), new Foo17(), new Foo18(), new Foo19(), new Foo20(), new Foo21(), new Foo22(), new Foo23(), new Foo24(), new Foo25(), new Foo26(), new Foo27(), new Foo28(), new Foo29(), new Foo30(), new Foo31(), new Foo32(), new Foo33(), new Foo34(), new Foo35(), new Foo36(), new Foo37(), new Foo38(), new Foo39(), new Foo40(), new Foo41(), new Foo42(), new Foo43(), new Foo44(), new Foo45(), new Foo46(), new Foo47(), new Foo48(), new Foo49(), new Foo50(), new Foo51(), new Foo52(), new Foo53(), new Foo54(), new Foo55(), new Foo56(), new Foo57(), new Foo58(), new Foo59(), new Foo60(), new Foo61(), new Foo62(), new Foo63(), new Foo64(), new Foo65(), new Foo66(), new Foo67(), new Foo68(), new Foo69(), new Foo70(), new Foo71(), new Foo72(), new Foo73(), new Foo74(), new Foo75(), new Foo76(), new Foo77(), new Foo78(), new Foo79(), new Foo80(), new Foo81(), new Foo82(), new Foo83(), new Foo84(), new Foo85(), new Foo86(), new Foo87(), new Foo88(), new Foo89(), new Foo90(), new Foo91(), new Foo92(), new Foo93(), new Foo94(), new Foo95(), new Foo96(), new Foo97(), new Foo98(), new Foo99()]; >+ >+ var fooIndex = 0; >+ for (var i = 0; i < 10000; ++i) { >+ var o = foos[fooIndex]; >+ for (var j = 0; j < 10; ++j) { >+ var result = o instanceof Foo42; >+ if (result != (fooIndex == 42)) >+ throw "Error: bad result at i = " + i + ": " + result; >+ } >+ fooIndex++; >+ if (fooIndex >= 100) >+ fooIndex = 0; >+ } >+})(); >Index: JSTests/microbenchmarks/path-specialization-loop-unswitching-big.js >=================================================================== >--- JSTests/microbenchmarks/path-specialization-loop-unswitching-big.js (nonexistent) >+++ JSTests/microbenchmarks/path-specialization-loop-unswitching-big.js (working copy) >@@ -0,0 +1,29 @@ >+// benefitOverCost = 1 >+ >+function foo(o) >+{ >+ var a = 0; >+ for (var i = 0; i < 100; ++i) { >+ var value; >+ if (o.p) >+ value = o.f; >+ else >+ value = o.g; >+ a += value; >+ a *= 1.5; >+ a += o.x; >+ if (o.p) >+ o.f = i; >+ else >+ o.g = i; >+ } >+ return a; >+} >+ >+noInline(foo); >+ >+for (var i = 0; i < 100000; ++i) { >+ var result = foo(i & 1 ? {p:true, f:42, x:1} : {p:false, x:1, g:42}); >+ if (result != 19514936521690325000) >+ throw "Error: bad result: " + result; >+} >Index: JSTests/microbenchmarks/path-specialization-loop-unswitching-impossible.js >=================================================================== >--- JSTests/microbenchmarks/path-specialization-loop-unswitching-impossible.js (nonexistent) >+++ JSTests/microbenchmarks/path-specialization-loop-unswitching-impossible.js (working copy) >@@ -0,0 +1,34 @@ >+// benefitOverCost = 1.1875 >+ >+function bar(value) >+{ >+ return value; >+} >+ >+noInline(bar); >+ >+function foo(o) >+{ >+ var a = 0; >+ for (var i = 0; i < 100; ++i) { >+ var value; >+ if (o.p) >+ value = o.f; >+ else >+ value = o.g; >+ a += bar(value); >+ if (o.p) >+ o.f = i; >+ else >+ o.g = i; >+ } >+ return a; >+} >+ >+noInline(foo); >+ >+for (var i = 0; i < 100000; ++i) { >+ var result = foo(i & 1 ? {p:true, f:42} : {p:false, g:42}); >+ if (result != 4893) >+ throw "Error: bad result: " + result; >+} >Index: JSTests/microbenchmarks/path-specialization-loop-unswitching.js >=================================================================== >--- JSTests/microbenchmarks/path-specialization-loop-unswitching.js (nonexistent) >+++ JSTests/microbenchmarks/path-specialization-loop-unswitching.js (working copy) >@@ -0,0 +1,29 @@ >+// benefitOverCost = 1.1875 >+ >+function foo(o) >+{ >+ var a = 0; >+ var b = 0; >+ for (var i = 0; i < 100; ++i) { >+ var value; >+ if (o.p) >+ value = o.f; >+ else >+ value = o.g; >+ a += value; >+ b += i; >+ if (o.p) >+ o.f = i; >+ else >+ o.g = i; >+ } >+ return a - b; >+} >+ >+noInline(foo); >+ >+for (var i = 0; i < 100000; ++i) { >+ var result = foo(i & 1 ? {p:true, f:42} : {p:false, g:42}); >+ if (result != -57) >+ throw "Error: bad result: " + result; >+} >Index: JSTests/microbenchmarks/path-specialization-multi-by-offset-smaller.js >=================================================================== >--- JSTests/microbenchmarks/path-specialization-multi-by-offset-smaller.js (nonexistent) >+++ JSTests/microbenchmarks/path-specialization-multi-by-offset-smaller.js (working copy) >@@ -0,0 +1,18 @@ >+// benefitOverCost = 2 >+ >+function foo(o) { >+ var result = 0; >+ for (var i = 0; i < 100; ++i) { >+ result = (result + o.f) | 0; >+ o.f = i; >+ } >+ return result; >+} >+ >+noInline(foo); >+ >+for (var i = 0; i < 100000; ++i) { >+ var result = foo(i & 1 ? {f:42} : {e:41, f:42}); >+ if (result != (99 * 98) / 2 + 42) >+ throw "Error: bad result: " + result; >+} >Index: JSTests/microbenchmarks/path-specialization-multi-by-offset.js >=================================================================== >--- JSTests/microbenchmarks/path-specialization-multi-by-offset.js (nonexistent) >+++ JSTests/microbenchmarks/path-specialization-multi-by-offset.js (working copy) >@@ -0,0 +1,18 @@ >+// benefitOverCost = 2 >+ >+function foo(o) { >+ var result = 0; >+ for (var i = 0; i < 100; ++i) { >+ result += o.f; >+ o.f = i; >+ } >+ return result; >+} >+ >+noInline(foo); >+ >+for (var i = 0; i < 100000; ++i) { >+ var result = foo(i & 1 ? {f:42} : {e:41, f:42}); >+ if (result != (99 * 98) / 2 + 42) >+ throw "Error: bad result: " + result; >+} >Index: JSTests/stress/instanceof-dynamic-proxy-check-structure.js >=================================================================== >--- JSTests/stress/instanceof-dynamic-proxy-check-structure.js (nonexistent) >+++ JSTests/stress/instanceof-dynamic-proxy-check-structure.js (working copy) >@@ -0,0 +1,159 @@ >+class Foo0 { } >+class Foo1 { } >+class Foo2 { } >+class Foo3 { } >+class Foo4 { } >+class Foo5 { } >+class Foo6 { } >+class Foo7 { } >+class Foo8 { } >+class Foo9 { } >+class Foo10 { } >+class Foo11 { } >+class Foo12 { } >+class Foo13 { } >+class Foo14 { } >+class Foo15 { } >+class Foo16 { } >+class Foo17 { } >+class Foo18 { } >+class Foo19 { } >+class Foo20 { } >+class Foo21 { } >+class Foo22 { } >+class Foo23 { } >+class Foo24 { } >+class Foo25 { } >+class Foo26 { } >+class Foo27 { } >+class Foo28 { } >+class Foo29 { } >+class Foo30 { } >+class Foo31 { } >+class Foo32 { } >+class Foo33 { } >+class Foo34 { } >+class Foo35 { } >+class Foo36 { } >+class Foo37 { } >+class Foo38 { } >+class Foo39 { } >+class Foo40 { } >+class Foo41 { } >+class Foo42 { } >+class Foo43 { } >+class Foo44 { } >+class Foo45 { } >+class Foo46 { } >+class Foo47 { } >+class Foo48 { } >+class Foo49 { } >+class Foo50 { } >+class Foo51 { } >+class Foo52 { } >+class Foo53 { } >+class Foo54 { } >+class Foo55 { } >+class Foo56 { } >+class Foo57 { } >+class Foo58 { } >+class Foo59 { } >+class Foo60 { } >+class Foo61 { } >+class Foo62 { } >+class Foo63 { } >+class Foo64 { } >+class Foo65 { } >+class Foo66 { } >+class Foo67 { } >+class Foo68 { } >+class Foo69 { } >+class Foo70 { } >+class Foo71 { } >+class Foo72 { } >+class Foo73 { } >+class Foo74 { } >+class Foo75 { } >+class Foo76 { } >+class Foo77 { } >+class Foo78 { } >+class Foo79 { } >+class Foo80 { } >+class Foo81 { } >+class Foo82 { } >+class Foo83 { } >+class Foo84 { } >+class Foo85 { } >+class Foo86 { } >+class Foo87 { } >+class Foo88 { } >+class Foo89 { } >+class Foo90 { } >+class Foo91 { } >+class Foo92 { } >+class Foo93 { } >+class Foo94 { } >+class Foo95 { } >+class Foo96 { } >+class Foo97 { } >+class Foo98 { } >+class Foo99 { } >+ >+var foos = [new Foo0(), new Foo1(), new Foo2(), new Foo3(), new Foo4(), new Foo5(), new Foo6(), new Foo7(), new Foo8(), new Foo9(), new Foo10(), new Foo11(), new Foo12(), new Foo13(), new Foo14(), new Foo15(), new Foo16(), new Foo17(), new Foo18(), new Foo19(), new Foo20(), new Foo21(), new Foo22(), new Foo23(), new Foo24(), new Foo25(), new Foo26(), new Foo27(), new Foo28(), new Foo29(), new Foo30(), new Foo31(), new Foo32(), new Foo33(), new Foo34(), new Foo35(), new Foo36(), new Foo37(), new Foo38(), new Foo39(), new Foo40(), new Foo41(), new Foo42(), new Foo43(), new Foo44(), new Foo45(), new Foo46(), new Foo47(), new Foo48(), new Foo49(), new Foo50(), new Foo51(), new Foo52(), new Foo53(), new Foo54(), new Foo55(), new Foo56(), new Foo57(), new Foo58(), new Foo59(), new Foo60(), new Foo61(), new Foo62(), new Foo63(), new Foo64(), new Foo65(), new Foo66(), new Foo67(), new Foo68(), new Foo69(), new Foo70(), new Foo71(), new Foo72(), new Foo73(), new Foo74(), new Foo75(), new Foo76(), new Foo77(), new Foo78(), new Foo79(), new Foo80(), new Foo81(), new Foo82(), new Foo83(), new Foo84(), new Foo85(), new Foo86(), new Foo87(), new Foo88(), new Foo89(), new Foo90(), new Foo91(), new Foo92(), new Foo93(), new Foo94(), new Foo95(), new Foo96(), new Foo97(), new Foo98(), new Foo99()]; >+ >+class Foo { } >+ >+function Bar() { } >+ >+var numberOfGetPrototypeOfCalls = 0; >+ >+var doBadThings = function() { }; >+ >+Bar.prototype = new Proxy( >+ {}, >+ { >+ getPrototypeOf() >+ { >+ numberOfGetPrototypeOfCalls++; >+ doBadThings(); >+ return Foo.prototype; >+ } >+ }); >+ >+// Break some watchpoints. >+var o = {f:42}; >+o.g = 43; >+ >+function foo(o, p, q) >+{ >+ var result = o.f; >+ var _ = p instanceof Foo; >+ q.f = 11; >+ return result + o.f; >+} >+ >+noInline(foo); >+ >+for (var i = 0; i < 10000; ++i) { >+ var result = foo({f:42}, foos[i % foos.length], {f:0}); >+ if (result != 84) >+ throw "Error: bad result in loop: " + result; >+} >+ >+var globalO = {f:42}; >+var didCallGetter = false; >+doBadThings = function() { >+ delete globalO.f; >+ globalO.__defineGetter__("f", function() { >+ didCallGetter = true; >+ return 43; >+ }); >+}; >+ >+var result = foo(globalO, new Bar(), {f:0}); >+if (result != 85) >+ throw "Error: bad result at end: " + result; >+if (!didCallGetter) >+ throw "Error: did not call getter"; >+if (numberOfGetPrototypeOfCalls != 1) >+ throw "Error: did not call getPrototypeOf() the right number of times at end"; >Index: JSTests/stress/instanceof-dynamic-proxy-loop.js >=================================================================== >--- JSTests/stress/instanceof-dynamic-proxy-loop.js (nonexistent) >+++ JSTests/stress/instanceof-dynamic-proxy-loop.js (working copy) >@@ -0,0 +1,159 @@ >+class Foo0 { } >+class Foo1 { } >+class Foo2 { } >+class Foo3 { } >+class Foo4 { } >+class Foo5 { } >+class Foo6 { } >+class Foo7 { } >+class Foo8 { } >+class Foo9 { } >+class Foo10 { } >+class Foo11 { } >+class Foo12 { } >+class Foo13 { } >+class Foo14 { } >+class Foo15 { } >+class Foo16 { } >+class Foo17 { } >+class Foo18 { } >+class Foo19 { } >+class Foo20 { } >+class Foo21 { } >+class Foo22 { } >+class Foo23 { } >+class Foo24 { } >+class Foo25 { } >+class Foo26 { } >+class Foo27 { } >+class Foo28 { } >+class Foo29 { } >+class Foo30 { } >+class Foo31 { } >+class Foo32 { } >+class Foo33 { } >+class Foo34 { } >+class Foo35 { } >+class Foo36 { } >+class Foo37 { } >+class Foo38 { } >+class Foo39 { } >+class Foo40 { } >+class Foo41 { } >+class Foo42 { } >+class Foo43 { } >+class Foo44 { } >+class Foo45 { } >+class Foo46 { } >+class Foo47 { } >+class Foo48 { } >+class Foo49 { } >+class Foo50 { } >+class Foo51 { } >+class Foo52 { } >+class Foo53 { } >+class Foo54 { } >+class Foo55 { } >+class Foo56 { } >+class Foo57 { } >+class Foo58 { } >+class Foo59 { } >+class Foo60 { } >+class Foo61 { } >+class Foo62 { } >+class Foo63 { } >+class Foo64 { } >+class Foo65 { } >+class Foo66 { } >+class Foo67 { } >+class Foo68 { } >+class Foo69 { } >+class Foo70 { } >+class Foo71 { } >+class Foo72 { } >+class Foo73 { } >+class Foo74 { } >+class Foo75 { } >+class Foo76 { } >+class Foo77 { } >+class Foo78 { } >+class Foo79 { } >+class Foo80 { } >+class Foo81 { } >+class Foo82 { } >+class Foo83 { } >+class Foo84 { } >+class Foo85 { } >+class Foo86 { } >+class Foo87 { } >+class Foo88 { } >+class Foo89 { } >+class Foo90 { } >+class Foo91 { } >+class Foo92 { } >+class Foo93 { } >+class Foo94 { } >+class Foo95 { } >+class Foo96 { } >+class Foo97 { } >+class Foo98 { } >+class Foo99 { } >+ >+var foos = [new Foo0(), new Foo1(), new Foo2(), new Foo3(), new Foo4(), new Foo5(), new Foo6(), new Foo7(), new Foo8(), new Foo9(), new Foo10(), new Foo11(), new Foo12(), new Foo13(), new Foo14(), new Foo15(), new Foo16(), new Foo17(), new Foo18(), new Foo19(), new Foo20(), new Foo21(), new Foo22(), new Foo23(), new Foo24(), new Foo25(), new Foo26(), new Foo27(), new Foo28(), new Foo29(), new Foo30(), new Foo31(), new Foo32(), new Foo33(), new Foo34(), new Foo35(), new Foo36(), new Foo37(), new Foo38(), new Foo39(), new Foo40(), new Foo41(), new Foo42(), new Foo43(), new Foo44(), new Foo45(), new Foo46(), new Foo47(), new Foo48(), new Foo49(), new Foo50(), new Foo51(), new Foo52(), new Foo53(), new Foo54(), new Foo55(), new Foo56(), new Foo57(), new Foo58(), new Foo59(), new Foo60(), new Foo61(), new Foo62(), new Foo63(), new Foo64(), new Foo65(), new Foo66(), new Foo67(), new Foo68(), new Foo69(), new Foo70(), new Foo71(), new Foo72(), new Foo73(), new Foo74(), new Foo75(), new Foo76(), new Foo77(), new Foo78(), new Foo79(), new Foo80(), new Foo81(), new Foo82(), new Foo83(), new Foo84(), new Foo85(), new Foo86(), new Foo87(), new Foo88(), new Foo89(), new Foo90(), new Foo91(), new Foo92(), new Foo93(), new Foo94(), new Foo95(), new Foo96(), new Foo97(), new Foo98(), new Foo99()]; >+ >+class Foo { } >+ >+function Bar() { } >+ >+var numberOfGetPrototypeOfCalls = 0; >+ >+var doBadThings = function() { }; >+ >+Bar.prototype = new Proxy( >+ {}, >+ { >+ getPrototypeOf() >+ { >+ numberOfGetPrototypeOfCalls++; >+ doBadThings(); >+ return Foo.prototype; >+ } >+ }); >+ >+// Break some watchpoints. >+var o = {f:42}; >+o.g = 43; >+ >+function foo(o, p) >+{ >+ var result = o.f; >+ for (var i = 0; i < 5; ++i) >+ var _ = p instanceof Foo; >+ return result + o.f; >+} >+ >+noInline(foo); >+ >+for (var i = 0; i < 10000; ++i) { >+ var result = foo({f:42}, foos[i % foos.length]); >+ if (result != 84) >+ throw "Error: bad result in loop: " + result; >+} >+ >+var globalO = {f:42}; >+var didCallGetter = false; >+doBadThings = function() { >+ delete globalO.f; >+ globalO.__defineGetter__("f", function() { >+ didCallGetter = true; >+ return 43; >+ }); >+}; >+ >+var result = foo(globalO, new Bar()); >+if (result != 85) >+ throw "Error: bad result at end: " + result; >+if (!didCallGetter) >+ throw "Error: did not call getter"; >+if (numberOfGetPrototypeOfCalls != 5) >+ throw "Error: did not call getPrototypeOf() the right number of times at end"; >Index: JSTests/stress/instanceof-dynamic-proxy.js >=================================================================== >--- JSTests/stress/instanceof-dynamic-proxy.js (nonexistent) >+++ JSTests/stress/instanceof-dynamic-proxy.js (working copy) >@@ -0,0 +1,150 @@ >+class Foo0 { } >+class Foo1 { } >+class Foo2 { } >+class Foo3 { } >+class Foo4 { } >+class Foo5 { } >+class Foo6 { } >+class Foo7 { } >+class Foo8 { } >+class Foo9 { } >+class Foo10 { } >+class Foo11 { } >+class Foo12 { } >+class Foo13 { } >+class Foo14 { } >+class Foo15 { } >+class Foo16 { } >+class Foo17 { } >+class Foo18 { } >+class Foo19 { } >+class Foo20 { } >+class Foo21 { } >+class Foo22 { } >+class Foo23 { } >+class Foo24 { } >+class Foo25 { } >+class Foo26 { } >+class Foo27 { } >+class Foo28 { } >+class Foo29 { } >+class Foo30 { } >+class Foo31 { } >+class Foo32 { } >+class Foo33 { } >+class Foo34 { } >+class Foo35 { } >+class Foo36 { } >+class Foo37 { } >+class Foo38 { } >+class Foo39 { } >+class Foo40 { } >+class Foo41 { } >+class Foo42 { } >+class Foo43 { } >+class Foo44 { } >+class Foo45 { } >+class Foo46 { } >+class Foo47 { } >+class Foo48 { } >+class Foo49 { } >+class Foo50 { } >+class Foo51 { } >+class Foo52 { } >+class Foo53 { } >+class Foo54 { } >+class Foo55 { } >+class Foo56 { } >+class Foo57 { } >+class Foo58 { } >+class Foo59 { } >+class Foo60 { } >+class Foo61 { } >+class Foo62 { } >+class Foo63 { } >+class Foo64 { } >+class Foo65 { } >+class Foo66 { } >+class Foo67 { } >+class Foo68 { } >+class Foo69 { } >+class Foo70 { } >+class Foo71 { } >+class Foo72 { } >+class Foo73 { } >+class Foo74 { } >+class Foo75 { } >+class Foo76 { } >+class Foo77 { } >+class Foo78 { } >+class Foo79 { } >+class Foo80 { } >+class Foo81 { } >+class Foo82 { } >+class Foo83 { } >+class Foo84 { } >+class Foo85 { } >+class Foo86 { } >+class Foo87 { } >+class Foo88 { } >+class Foo89 { } >+class Foo90 { } >+class Foo91 { } >+class Foo92 { } >+class Foo93 { } >+class Foo94 { } >+class Foo95 { } >+class Foo96 { } >+class Foo97 { } >+class Foo98 { } >+class Foo99 { } >+ >+var foos = [new Foo0(), new Foo1(), new Foo2(), new Foo3(), new Foo4(), new Foo5(), new Foo6(), new Foo7(), new Foo8(), new Foo9(), new Foo10(), new Foo11(), new Foo12(), new Foo13(), new Foo14(), new Foo15(), new Foo16(), new Foo17(), new Foo18(), new Foo19(), new Foo20(), new Foo21(), new Foo22(), new Foo23(), new Foo24(), new Foo25(), new Foo26(), new Foo27(), new Foo28(), new Foo29(), new Foo30(), new Foo31(), new Foo32(), new Foo33(), new Foo34(), new Foo35(), new Foo36(), new Foo37(), new Foo38(), new Foo39(), new Foo40(), new Foo41(), new Foo42(), new Foo43(), new Foo44(), new Foo45(), new Foo46(), new Foo47(), new Foo48(), new Foo49(), new Foo50(), new Foo51(), new Foo52(), new Foo53(), new Foo54(), new Foo55(), new Foo56(), new Foo57(), new Foo58(), new Foo59(), new Foo60(), new Foo61(), new Foo62(), new Foo63(), new Foo64(), new Foo65(), new Foo66(), new Foo67(), new Foo68(), new Foo69(), new Foo70(), new Foo71(), new Foo72(), new Foo73(), new Foo74(), new Foo75(), new Foo76(), new Foo77(), new Foo78(), new Foo79(), new Foo80(), new Foo81(), new Foo82(), new Foo83(), new Foo84(), new Foo85(), new Foo86(), new Foo87(), new Foo88(), new Foo89(), new Foo90(), new Foo91(), new Foo92(), new Foo93(), new Foo94(), new Foo95(), new Foo96(), new Foo97(), new Foo98(), new Foo99()]; >+ >+class Foo { } >+ >+function Bar() { } >+ >+var numberOfGetPrototypeOfCalls = 0; >+ >+var doBadThings = function() { }; >+ >+Bar.prototype = new Proxy( >+ {}, >+ { >+ getPrototypeOf() >+ { >+ numberOfGetPrototypeOfCalls++; >+ doBadThings(); >+ return Foo.prototype; >+ } >+ }); >+ >+var o = {f:42}; >+ >+function foo(o, p) >+{ >+ var result = o.f; >+ var _ = p instanceof Foo; >+ return result + o.f; >+} >+ >+noInline(foo); >+ >+for (var i = 0; i < 10000; ++i) { >+ var result = foo({f:42}, foos[i % foos.length]); >+ if (result != 84) >+ throw "Error: bad result in loop: " + result; >+} >+ >+var globalO = {f:42}; >+var didCallGetter = false; >+doBadThings = function() { >+ globalO.f = 43; >+}; >+ >+var result = foo(globalO, new Bar()); >+if (result != 85) >+ throw "Error: bad result at end: " + result; >+if (numberOfGetPrototypeOfCalls != 1) >+ throw "Error: did not call getPrototypeOf() the right number of times at end"; >Index: JSTests/stress/instanceof-hit-one-object-then-another.js >=================================================================== >--- JSTests/stress/instanceof-hit-one-object-then-another.js (nonexistent) >+++ JSTests/stress/instanceof-hit-one-object-then-another.js (working copy) >@@ -0,0 +1,19 @@ >+function foo(o, p) >+{ >+ return o instanceof p; >+} >+ >+noInline(foo); >+ >+class Foo { } >+class Bar { } >+ >+for (var i = 0; i < 10000; ++i) { >+ var result = foo(new Foo(), Foo); >+ if (!result) >+ throw "Error: bad result in loop: " + result; >+} >+ >+var result = foo(new Foo(), Bar); >+if (result) >+ throw "Error: bad result at end: " + result; >Index: JSTests/stress/instanceof-hit-two-objects-then-another.js >=================================================================== >--- JSTests/stress/instanceof-hit-two-objects-then-another.js (nonexistent) >+++ JSTests/stress/instanceof-hit-two-objects-then-another.js (working copy) >@@ -0,0 +1,23 @@ >+function foo(o, p) >+{ >+ return o instanceof p; >+} >+ >+noInline(foo); >+ >+class Foo { } >+class Bar { } >+class Baz { } >+ >+for (var i = 0; i < 10000; ++i) { >+ var result = foo(new Foo(), Foo); >+ if (!result) >+ throw "Error: bad result in loop (1): " + result; >+ var result = foo(new Foo(), Bar); >+ if (result) >+ throw "Error: bad result in loop (2): " + result; >+} >+ >+var result = foo(new Foo(), Baz); >+if (result) >+ throw "Error: bad result at end: " + result; >Index: JSTests/stress/instanceof-prototype-change.js >=================================================================== >--- JSTests/stress/instanceof-prototype-change.js (nonexistent) >+++ JSTests/stress/instanceof-prototype-change.js (working copy) >@@ -0,0 +1,25 @@ >+function foo(o, p) >+{ >+ return o instanceof p; >+} >+ >+noInline(foo); >+ >+class Foo { } >+ >+function Bar() { } >+Bar.prototype = new Foo(); >+ >+new Foo().thingy = 42; >+ >+for (var i = 0; i < 10000; ++i) { >+ var result = foo(new Bar(), Foo); >+ if (!result) >+ throw "Error: bad result in loop: " + result; >+} >+ >+Bar.prototype.__proto__ = {} >+ >+var result = foo(new Bar(), Foo); >+if (result) >+ throw "Error: bad result at end: " + result; >Index: JSTests/stress/string-instanceof.js >=================================================================== >--- JSTests/stress/string-instanceof.js (nonexistent) >+++ JSTests/stress/string-instanceof.js (working copy) >@@ -0,0 +1,13 @@ >+function foo(value, proto) >+{ >+ return value instanceof proto; >+} >+ >+noInline(foo); >+ >+for (var i = 0; i < 10000; ++i) { >+ var result = foo("hello", String); >+ if (result) >+ throw "Error: bad result: " + result; >+} >+ >Index: JSTests/stress/symbol-instanceof.js >=================================================================== >--- JSTests/stress/symbol-instanceof.js (nonexistent) >+++ JSTests/stress/symbol-instanceof.js (working copy) >@@ -0,0 +1,13 @@ >+function foo(value, proto) >+{ >+ return value instanceof proto; >+} >+ >+noInline(foo); >+ >+for (var i = 0; i < 10000; ++i) { >+ var result = foo(Symbol("hello"), Symbol); >+ if (result) >+ throw "Error: bad result: " + result; >+} >+ >Index: Source/JavaScriptCore/ChangeLog >=================================================================== >--- Source/JavaScriptCore/ChangeLog (revision 231917) >+++ Source/JavaScriptCore/ChangeLog (working copy) >@@ -1,3 +1,137 @@ >+2018-05-16 Filip Pizlo <fpizlo@apple.com> >+ >+ JSC should have InstanceOf inline caching >+ https://bugs.webkit.org/show_bug.cgi?id=185652 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ This adds a polymorphic inline cache for instanceof. It caches hits and misses. It uses the >+ existing PolymorphicAccess IC machinery along with all of its heuristics. If we ever generate >+ too many cases, we emit the generic instanceof implementation instead. >+ >+ All of the JIT tiers use the same InstanceOf IC. It uses the existing JITInlineCacheGenerator >+ abstraction. >+ >+ * API/tests/testapi.mm: >+ (testObjectiveCAPIMain): >+ * JavaScriptCore.xcodeproj/project.pbxproj: >+ * Sources.txt: >+ * b3/B3Effects.h: >+ (JSC::B3::Effects::forReadOnlyCall): >+ * bytecode/AccessCase.cpp: >+ (JSC::AccessCase::guardedByStructureCheck const): >+ (JSC::AccessCase::canReplace const): >+ (JSC::AccessCase::visitWeak const): >+ (JSC::AccessCase::generateWithGuard): >+ (JSC::AccessCase::generateImpl): >+ * bytecode/AccessCase.h: >+ * bytecode/InstanceOfAccessCase.cpp: Added. >+ (JSC::InstanceOfAccessCase::create): >+ (JSC::InstanceOfAccessCase::dumpImpl const): >+ (JSC::InstanceOfAccessCase::clone const): >+ (JSC::InstanceOfAccessCase::~InstanceOfAccessCase): >+ (JSC::InstanceOfAccessCase::InstanceOfAccessCase): >+ * bytecode/InstanceOfAccessCase.h: Added. >+ (JSC::InstanceOfAccessCase::prototype const): >+ * bytecode/ObjectPropertyCondition.h: >+ (JSC::ObjectPropertyCondition::hasPrototypeWithoutBarrier): >+ (JSC::ObjectPropertyCondition::hasPrototype): >+ * bytecode/ObjectPropertyConditionSet.cpp: >+ (JSC::generateConditionsForInstanceOf): >+ * bytecode/ObjectPropertyConditionSet.h: >+ * bytecode/PolymorphicAccess.cpp: >+ (JSC::PolymorphicAccess::addCases): >+ (JSC::PolymorphicAccess::regenerate): >+ (WTF::printInternal): >+ * bytecode/PropertyCondition.cpp: >+ (JSC::PropertyCondition::dumpInContext const): >+ (JSC::PropertyCondition::isStillValidAssumingImpurePropertyWatchpoint const): >+ (JSC::PropertyCondition::validityRequiresImpurePropertyWatchpoint const): >+ (WTF::printInternal): >+ * bytecode/PropertyCondition.h: >+ (JSC::PropertyCondition::absenceWithoutBarrier): >+ (JSC::PropertyCondition::absenceOfSetEffectWithoutBarrier): >+ (JSC::PropertyCondition::hasPrototypeWithoutBarrier): >+ (JSC::PropertyCondition::hasPrototype): >+ (JSC::PropertyCondition::hasPrototype const): >+ (JSC::PropertyCondition::prototype const): >+ (JSC::PropertyCondition::hash const): >+ (JSC::PropertyCondition::operator== const): >+ * bytecode/StructureStubInfo.cpp: >+ (JSC::StructureStubInfo::StructureStubInfo): >+ (JSC::StructureStubInfo::reset): >+ * bytecode/StructureStubInfo.h: >+ (JSC::StructureStubInfo::considerCaching): >+ * dfg/DFGByteCodeParser.cpp: >+ (JSC::DFG::ByteCodeParser::parseBlock): >+ * dfg/DFGFixupPhase.cpp: >+ (JSC::DFG::FixupPhase::fixupNode): >+ * dfg/DFGInlineCacheWrapper.h: >+ * dfg/DFGInlineCacheWrapperInlines.h: >+ (JSC::DFG::InlineCacheWrapper<GeneratorType>::finalize): >+ * dfg/DFGJITCompiler.cpp: >+ (JSC::DFG::JITCompiler::link): >+ * dfg/DFGJITCompiler.h: >+ (JSC::DFG::JITCompiler::addInstanceOf): >+ * dfg/DFGOperations.cpp: >+ * dfg/DFGSpeculativeJIT.cpp: >+ (JSC::DFG::SpeculativeJIT::usedRegisters): >+ (JSC::DFG::SpeculativeJIT::compileInstanceOfForCells): >+ (JSC::DFG::SpeculativeJIT::compileInstanceOf): >+ (JSC::DFG::SpeculativeJIT::compileInstanceOfForObject): Deleted. >+ * dfg/DFGSpeculativeJIT.h: >+ * dfg/DFGSpeculativeJIT64.cpp: >+ (JSC::DFG::SpeculativeJIT::cachedGetById): >+ (JSC::DFG::SpeculativeJIT::cachedGetByIdWithThis): >+ * ftl/FTLLowerDFGToB3.cpp: >+ (JSC::FTL::DFG::LowerDFGToB3::compileAssertNotEmpty): >+ (JSC::FTL::DFG::LowerDFGToB3::compilePutById): >+ (JSC::FTL::DFG::LowerDFGToB3::compileNumberIsInteger): >+ (JSC::FTL::DFG::LowerDFGToB3::compileIn): >+ (JSC::FTL::DFG::LowerDFGToB3::compileInstanceOf): >+ (JSC::FTL::DFG::LowerDFGToB3::getById): >+ (JSC::FTL::DFG::LowerDFGToB3::getByIdWithThis): >+ * jit/ICStats.h: >+ * jit/JIT.cpp: >+ (JSC::JIT::privateCompileSlowCases): >+ (JSC::JIT::link): >+ * jit/JIT.h: >+ * jit/JITInlineCacheGenerator.cpp: >+ (JSC::JITInlineCacheGenerator::JITInlineCacheGenerator): >+ (JSC::JITInlineCacheGenerator::finalize): >+ (JSC::JITByIdGenerator::JITByIdGenerator): >+ (JSC::JITByIdGenerator::finalize): >+ (JSC::JITInstanceOfGenerator::JITInstanceOfGenerator): >+ (JSC::JITInstanceOfGenerator::generateFastPath): >+ (JSC::JITInstanceOfGenerator::finalize): >+ * jit/JITInlineCacheGenerator.h: >+ (JSC::JITInlineCacheGenerator::reportSlowPathCall): >+ (JSC::JITInlineCacheGenerator::slowPathBegin const): >+ (JSC::JITInstanceOfGenerator::JITInstanceOfGenerator): >+ (JSC::finalizeInlineCaches): >+ (JSC::JITByIdGenerator::reportSlowPathCall): Deleted. >+ (JSC::JITByIdGenerator::slowPathBegin const): Deleted. >+ * jit/JITOpcodes.cpp: >+ (JSC::JIT::emit_op_instanceof): >+ (JSC::JIT::emitSlow_op_instanceof): >+ * jit/JITOperations.cpp: >+ * jit/JITOperations.h: >+ * jit/JITPropertyAccess.cpp: >+ (JSC::JIT::privateCompileGetByValWithCachedId): >+ (JSC::JIT::privateCompilePutByValWithCachedId): >+ * jit/RegisterSet.cpp: >+ (JSC::RegisterSet::stubUnavailableRegisters): >+ * jit/Repatch.cpp: >+ (JSC::tryCacheIn): >+ (JSC::tryCacheInstanceOf): >+ (JSC::repatchInstanceOf): >+ (JSC::resetPatchableJump): >+ (JSC::resetIn): >+ (JSC::resetInstanceOf): >+ * jit/Repatch.h: >+ * runtime/Options.h: >+ * runtime/Structure.h: >+ > 2018-05-17 Yusuke Suzuki <utatane.tea@gmail.com> > > [JSC] Use AssemblyHelpers' type checking functions as much as possible >Index: Source/JavaScriptCore/Sources.txt >=================================================================== >--- Source/JavaScriptCore/Sources.txt (revision 231917) >+++ Source/JavaScriptCore/Sources.txt (working copy) >@@ -222,6 +222,7 @@ bytecode/GetterSetterAccessCase.cpp > bytecode/InlineAccess.cpp > bytecode/InlineCallFrame.cpp > bytecode/InlineCallFrameSet.cpp >+bytecode/InstanceOfAccessCase.cpp > bytecode/IntrinsicGetterAccessCase.cpp > bytecode/JumpTable.cpp > bytecode/LLIntPrototypeLoadAdaptiveStructureWatchpoint.cpp >Index: Source/JavaScriptCore/API/tests/testapi.mm >=================================================================== >--- Source/JavaScriptCore/API/tests/testapi.mm (revision 231917) >+++ Source/JavaScriptCore/API/tests/testapi.mm (working copy) >@@ -1472,33 +1472,6 @@ static void testObjectiveCAPIMain() > checkResult(@"Ran code in five concurrent VMs that GC'd", ok); > } > >- @autoreleasepool { >- JSContext *context = [[JSContext alloc] init]; >- JSVirtualMachine *vm = [context virtualMachine]; >- [vm shrinkFootprint]; // Make sure that when we allocate a ton of memory below we reuse at little as possible. >- >- std::optional<size_t> footprintBefore = WTF::memoryFootprint(); >- RELEASE_ASSERT(footprintBefore); >- >- [context evaluateScript:@"for (let i = 0; i < 10000; ++i) { eval(`myVariable_${i} = [i]`); }"]; >- >- static constexpr size_t approximateBytes = 10000 * sizeof(int); >- std::optional<size_t> footprintMiddle = WTF::memoryFootprint(); >- RELEASE_ASSERT(footprintMiddle); >- checkResult(@"Footprint is larger than what we allocated", *footprintMiddle > approximateBytes); >- checkResult(@"Footprint got larger as we allocated a ton of stuff", *footprintMiddle > *footprintBefore); >- size_t allocationDelta = *footprintMiddle - *footprintBefore; >- checkResult(@"We allocated as much or more memory than what we expected to", allocationDelta >= approximateBytes); >- >- [context evaluateScript:@"for (let i = 0; i < 10000; ++i) { eval(`myVariable_${i} = null`); }"]; >- [vm shrinkFootprint]; >- std::optional<size_t> footprintAfter = WTF::memoryFootprint(); >- RELEASE_ASSERT(footprintAfter); >- checkResult(@"Footprint got smaller after we shrank the VM", *footprintAfter < *footprintMiddle); >- size_t freeDelta = *footprintMiddle - *footprintAfter; >- checkResult(@"Shrinking the footprint of the VM actually freed memory", freeDelta > (approximateBytes / 2)); >- } >- > currentThisInsideBlockGetterTest(); > runDateTests(); > runJSExportTests(); >Index: Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj >=================================================================== >--- Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj (revision 231917) >+++ Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj (working copy) >@@ -310,6 +310,7 @@ > 0F485322187750560083B687 /* DFGArithMode.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F485320187750560083B687 /* DFGArithMode.h */; }; > 0F485328187DFDEC0083B687 /* FTLAvailableRecovery.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F485324187DFDEC0083B687 /* FTLAvailableRecovery.h */; settings = {ATTRIBUTES = (Private, ); }; }; > 0F48532A187DFDEC0083B687 /* FTLRecoveryOpcode.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F485326187DFDEC0083B687 /* FTLRecoveryOpcode.h */; settings = {ATTRIBUTES = (Private, ); }; }; >+ 0F49E9AA20AB4D00001CA0AA /* InstanceOfAccessCase.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F49E9A820AB4CFB001CA0AA /* InstanceOfAccessCase.h */; }; > 0F4A38FA1C8E13DF00190318 /* SuperSampler.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F4A38F81C8E13DF00190318 /* SuperSampler.h */; settings = {ATTRIBUTES = (Private, ); }; }; > 0F4AE0431FE0D25700E20839 /* InferredValueInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F4AE0421FE0D25400E20839 /* InferredValueInlines.h */; }; > 0F4B94DC17B9F07500DD03A4 /* TypedArrayInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F4B94DB17B9F07500DD03A4 /* TypedArrayInlines.h */; settings = {ATTRIBUTES = (Private, ); }; }; >@@ -2299,6 +2300,8 @@ > 0F485325187DFDEC0083B687 /* FTLRecoveryOpcode.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FTLRecoveryOpcode.cpp; path = ftl/FTLRecoveryOpcode.cpp; sourceTree = "<group>"; }; > 0F485326187DFDEC0083B687 /* FTLRecoveryOpcode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FTLRecoveryOpcode.h; path = ftl/FTLRecoveryOpcode.h; sourceTree = "<group>"; }; > 0F493AF816D0CAD10084508B /* SourceProvider.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SourceProvider.cpp; sourceTree = "<group>"; }; >+ 0F49E9A720AB4CFB001CA0AA /* InstanceOfAccessCase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = InstanceOfAccessCase.cpp; sourceTree = "<group>"; }; >+ 0F49E9A820AB4CFB001CA0AA /* InstanceOfAccessCase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InstanceOfAccessCase.h; sourceTree = "<group>"; }; > 0F4A38F71C8E13DF00190318 /* SuperSampler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SuperSampler.cpp; sourceTree = "<group>"; }; > 0F4A38F81C8E13DF00190318 /* SuperSampler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SuperSampler.h; sourceTree = "<group>"; }; > 0F4AE0421FE0D25400E20839 /* InferredValueInlines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InferredValueInlines.h; sourceTree = "<group>"; }; >@@ -5962,6 +5965,8 @@ > 534E034D1E4D4B1600213F64 /* AccessCase.h */, > 53B0BE331E561AC900A8FC29 /* GetterSetterAccessCase.cpp */, > 534E03571E53BF2F00213F64 /* GetterSetterAccessCase.h */, >+ 0F49E9A720AB4CFB001CA0AA /* InstanceOfAccessCase.cpp */, >+ 0F49E9A820AB4CFB001CA0AA /* InstanceOfAccessCase.h */, > 53B0BE371E561B2400A8FC29 /* IntrinsicGetterAccessCase.cpp */, > 534E03531E53BD2900213F64 /* IntrinsicGetterAccessCase.h */, > 53B0BE351E561B0900A8FC29 /* ProxyableAccessCase.cpp */, >@@ -9208,6 +9213,7 @@ > 7C008CE7187631B600955C24 /* Microtask.h in Headers */, > FE2A87601F02381600EB31B2 /* MinimumReservedZoneSize.h in Headers */, > 86C568E211A213EE0007F7F0 /* MIPSAssembler.h in Headers */, >+ 0F49E9AA20AB4D00001CA0AA /* InstanceOfAccessCase.h in Headers */, > C4703CD7192844CC0013FBEA /* models.py in Headers */, > E3794E761B77EB97005543AE /* ModuleAnalyzer.h in Headers */, > 9F63434577274FAFB9336C38 /* ModuleNamespaceAccessCase.h in Headers */, >Index: Source/JavaScriptCore/b3/B3Effects.h >=================================================================== >--- Source/JavaScriptCore/b3/B3Effects.h (revision 231917) >+++ Source/JavaScriptCore/b3/B3Effects.h (working copy) >@@ -99,6 +99,17 @@ struct Effects { > result.fence = true; > return result; > } >+ >+ static Effects forReadOnlyCall() >+ { >+ Effects result; >+ result.exitsSideways = true; >+ result.controlDependent = true; >+ result.reads = HeapRange::top(); >+ result.readsPinned = true; >+ result.fence = true; >+ return result; >+ } > > static Effects forCheck() > { >Index: Source/JavaScriptCore/bytecode/AccessCase.cpp >=================================================================== >--- Source/JavaScriptCore/bytecode/AccessCase.cpp (revision 231917) >+++ Source/JavaScriptCore/bytecode/AccessCase.cpp (working copy) >@@ -35,6 +35,7 @@ > #include "GetterSetter.h" > #include "GetterSetterAccessCase.h" > #include "HeapInlines.h" >+#include "InstanceOfAccessCase.h" > #include "IntrinsicGetterAccessCase.h" > #include "JSCJSValueInlines.h" > #include "JSModuleEnvironment.h" >@@ -76,6 +77,7 @@ std::unique_ptr<AccessCase> AccessCase:: > case ScopedArgumentsLength: > case ModuleNamespaceLoad: > case Replace: >+ case InstanceOfGeneric: > RELEASE_ASSERT(!prototypeAccessChain); > break; > default: >@@ -148,10 +150,12 @@ Vector<WatchpointSet*, 2> AccessCase::co > Vector<WatchpointSet*, 2> result; > Structure* structure = this->structure(); > >- if ((structure && structure->needImpurePropertyWatchpoint()) >- || m_conditionSet.needImpurePropertyWatchpoint() >- || (m_polyProtoAccessChain && m_polyProtoAccessChain->needImpurePropertyWatchpoint())) >- result.append(vm.ensureWatchpointSetForImpureProperty(ident)); >+ if (!ident.isNull()) { >+ if ((structure && structure->needImpurePropertyWatchpoint()) >+ || m_conditionSet.needImpurePropertyWatchpoint() >+ || (m_polyProtoAccessChain && m_polyProtoAccessChain->needImpurePropertyWatchpoint())) >+ result.append(vm.ensureWatchpointSetForImpureProperty(ident)); >+ } > > if (additionalSet()) > result.append(additionalSet()); >@@ -183,6 +187,9 @@ bool AccessCase::guardedByStructureCheck > case DirectArgumentsLength: > case ScopedArgumentsLength: > case ModuleNamespaceLoad: >+ case InstanceOfHit: >+ case InstanceOfMiss: >+ case InstanceOfGeneric: > return false; > default: > return true; >@@ -221,7 +228,10 @@ bool AccessCase::canReplace(const Access > { > // This puts in a good effort to try to figure out if 'other' is made superfluous by '*this'. > // It's fine for this to return false if it's in doubt. >- >+ // >+ // Note that if A->guardedByStructureCheck() && B->guardedByStructureCheck() then >+ // A->canReplace(B) == B->canReplace(A). >+ > switch (type()) { > case ArrayLength: > case StringLength: >@@ -235,6 +245,25 @@ bool AccessCase::canReplace(const Access > auto& otherCase = this->as<ModuleNamespaceAccessCase>(); > return thisCase.moduleNamespaceObject() == otherCase.moduleNamespaceObject(); > } >+ case InstanceOfHit: >+ case InstanceOfMiss: { >+ if (other.type() != type()) >+ return false; >+ >+ if (this->as<InstanceOfAccessCase>().prototype() != other.as<InstanceOfAccessCase>().prototype()) >+ return false; >+ >+ return structure() == other.structure(); >+ } >+ case InstanceOfGeneric: >+ switch (other.type()) { >+ case InstanceOfGeneric: >+ case InstanceOfHit: >+ case InstanceOfMiss: >+ return true; >+ default: >+ return false; >+ } > default: > if (other.type() != type()) > return false; >@@ -311,6 +340,9 @@ bool AccessCase::visitWeak(VM& vm) const > return false; > if (accessCase.moduleEnvironment() && !Heap::isMarked(accessCase.moduleEnvironment())) > return false; >+ } else if (type() == InstanceOfHit || type() == InstanceOfMiss) { >+ if (as<InstanceOfAccessCase>().prototype() && !Heap::isMarked(as<InstanceOfAccessCase>().prototype())) >+ return false; > } > > return true; >@@ -351,13 +383,80 @@ void AccessCase::generateWithGuard( > m_state = Generated; > > CCallHelpers& jit = *state.jit; >+ StructureStubInfo& stubInfo = *state.stubInfo; > VM& vm = state.m_vm; > JSValueRegs valueRegs = state.valueRegs; > GPRReg baseGPR = state.baseGPR; >+ GPRReg thisGPR = state.thisGPR != InvalidGPRReg ? state.thisGPR : baseGPR; > GPRReg scratchGPR = state.scratchGPR; > > UNUSED_PARAM(vm); > >+ auto emitDefaultGuard = [&] () { >+ if (m_polyProtoAccessChain) { >+ GPRReg baseForAccessGPR = state.scratchGPR; >+ jit.move(state.baseGPR, baseForAccessGPR); >+ m_polyProtoAccessChain->forEach(structure(), [&] (Structure* structure, bool atEnd) { >+ fallThrough.append( >+ jit.branchStructure( >+ CCallHelpers::NotEqual, >+ CCallHelpers::Address(baseForAccessGPR, JSCell::structureIDOffset()), >+ structure)); >+ if (atEnd) { >+ if ((m_type == Miss || m_type == InMiss || m_type == Transition) && structure->hasPolyProto()) { >+ // For a Miss/InMiss/Transition, we must ensure we're at the end when the last item is poly proto. >+ // Transitions must do this because they need to verify there isn't a setter in the chain. >+ // Miss/InMiss need to do this to ensure there isn't a new item at the end of the chain that >+ // has the property. >+#if USE(JSVALUE64) >+ jit.load64(MacroAssembler::Address(baseForAccessGPR, offsetRelativeToBase(knownPolyProtoOffset)), baseForAccessGPR); >+ fallThrough.append(jit.branch64(CCallHelpers::NotEqual, baseForAccessGPR, CCallHelpers::TrustedImm64(ValueNull))); >+#else >+ jit.load32(MacroAssembler::Address(baseForAccessGPR, offsetRelativeToBase(knownPolyProtoOffset) + PayloadOffset), baseForAccessGPR); >+ fallThrough.append(jit.branchTestPtr(CCallHelpers::NonZero, baseForAccessGPR)); >+#endif >+ } >+ } else { >+ if (structure->hasMonoProto()) { >+ JSValue prototype = structure->prototypeForLookup(state.m_globalObject); >+ RELEASE_ASSERT(prototype.isObject()); >+ jit.move(CCallHelpers::TrustedImmPtr(asObject(prototype)), baseForAccessGPR); >+ } else { >+ RELEASE_ASSERT(structure->isObject()); // Primitives must have a stored prototype. We use prototypeForLookup for them. >+#if USE(JSVALUE64) >+ jit.load64(MacroAssembler::Address(baseForAccessGPR, offsetRelativeToBase(knownPolyProtoOffset)), baseForAccessGPR); >+ fallThrough.append(jit.branch64(CCallHelpers::Equal, baseForAccessGPR, CCallHelpers::TrustedImm64(ValueNull))); >+#else >+ jit.load32(MacroAssembler::Address(baseForAccessGPR, offsetRelativeToBase(knownPolyProtoOffset) + PayloadOffset), baseForAccessGPR); >+ fallThrough.append(jit.branchTestPtr(CCallHelpers::Zero, baseForAccessGPR)); >+#endif >+ } >+ } >+ }); >+ return; >+ } >+ >+ if (viaProxy()) { >+ fallThrough.append( >+ jit.branchIfNotType(baseGPR, PureForwardingProxyType)); >+ >+ jit.loadPtr(CCallHelpers::Address(baseGPR, JSProxy::targetOffset()), scratchGPR); >+ >+ fallThrough.append( >+ jit.branchStructure( >+ CCallHelpers::NotEqual, >+ CCallHelpers::Address(scratchGPR, JSCell::structureIDOffset()), >+ structure())); >+ return; >+ } >+ >+ fallThrough.append( >+ jit.branchStructure( >+ CCallHelpers::NotEqual, >+ CCallHelpers::Address(baseGPR, JSCell::structureIDOffset()), >+ structure())); >+ }; >+ > switch (m_type) { > case ArrayLength: { > ASSERT(!viaProxy()); >@@ -421,69 +520,101 @@ void AccessCase::generateWithGuard( > return; > } > >- default: { >- if (m_polyProtoAccessChain) { >- GPRReg baseForAccessGPR = state.scratchGPR; >- jit.move(state.baseGPR, baseForAccessGPR); >- m_polyProtoAccessChain->forEach(structure(), [&] (Structure* structure, bool atEnd) { >- fallThrough.append( >- jit.branchStructure( >- CCallHelpers::NotEqual, >- CCallHelpers::Address(baseForAccessGPR, JSCell::structureIDOffset()), >- structure)); >- if (atEnd) { >- if ((m_type == Miss || m_type == InMiss || m_type == Transition) && structure->hasPolyProto()) { >- // For a Miss/InMiss/Transition, we must ensure we're at the end when the last item is poly proto. >- // Transitions must do this because they need to verify there isn't a setter in the chain. >- // Miss/InMiss need to do this to ensure there isn't a new item at the end of the chain that >- // has the property. >+ case InstanceOfHit: >+ case InstanceOfMiss: >+ emitDefaultGuard(); >+ >+ fallThrough.append( >+ jit.branchPtr( >+ CCallHelpers::NotEqual, thisGPR, >+ CCallHelpers::TrustedImmPtr(as<InstanceOfAccessCase>().prototype()))); >+ break; >+ >+ case InstanceOfGeneric: { >+ // Legend: value = `base instanceof this`. >+ >+ GPRReg valueGPR = valueRegs.payloadGPR(); >+ >+ ScratchRegisterAllocator allocator(stubInfo.patch.usedRegisters); >+ allocator.lock(baseGPR); >+ allocator.lock(valueGPR); >+ allocator.lock(thisGPR); >+ allocator.lock(scratchGPR); >+ >+ GPRReg scratch2GPR = allocator.allocateScratchGPR(); >+ >+ if (!state.stubInfo->prototypeIsKnownObject) >+ state.failAndIgnore.append(jit.branchIfNotObject(thisGPR)); >+ >+ ScratchRegisterAllocator::PreservedState preservedState = >+ allocator.preserveReusedRegistersByPushing( >+ jit, >+ ScratchRegisterAllocator::ExtraStackSpace::NoExtraSpace); >+ CCallHelpers::Jump failAndIgnore; >+ >+ jit.move(baseGPR, valueGPR); >+ >+ CCallHelpers::Label loop(&jit); >+ failAndIgnore = jit.branch8( >+ CCallHelpers::Equal, >+ CCallHelpers::Address(valueGPR, JSCell::typeInfoTypeOffset()), >+ CCallHelpers::TrustedImm32(ProxyObjectType)); >+ >+ jit.emitLoadStructure(vm, valueGPR, scratch2GPR, scratchGPR); > #if USE(JSVALUE64) >- jit.load64(MacroAssembler::Address(baseForAccessGPR, offsetRelativeToBase(knownPolyProtoOffset)), baseForAccessGPR); >- fallThrough.append(jit.branchIfNotNull(baseForAccessGPR)); >+ jit.load64(CCallHelpers::Address(scratch2GPR, Structure::prototypeOffset()), scratch2GPR); >+ CCallHelpers::Jump hasMonoProto = jit.branchTest64(CCallHelpers::NonZero, scratch2GPR); >+ jit.load64( >+ CCallHelpers::Address(valueGPR, offsetRelativeToBase(knownPolyProtoOffset)), >+ scratch2GPR); >+ hasMonoProto.link(&jit); > #else >- jit.load32(MacroAssembler::Address(baseForAccessGPR, offsetRelativeToBase(knownPolyProtoOffset) + PayloadOffset), baseForAccessGPR); >- fallThrough.append(jit.branchTestPtr(CCallHelpers::NonZero, baseForAccessGPR)); >+ jit.load32( >+ CCallHelpers::Address(scratch2GPR, Structure::prototypeOffset() + TagOffset), >+ scratchGPR); >+ jit.load32( >+ CCallHelpers::Address(scratch2GPR, Structure::prototypeOffset() + PayloadOffset), >+ scratch2GPR); >+ CCallHelpers::Jump hasMonoProto = jit.branch32( >+ CCallHelpers::NotEqual, scratchGPR, CCallHelpers::TrustedImm32(JSValue::EmptyValueTag)); >+ jit.load32( >+ CCallHelpers::Address( >+ valueGPR, offsetRelativeToBase(knownPolyProtoOffset) + PayloadOffset), >+ scratch2GPR); >+ hasMonoProto.link(&jit); > #endif >- } >- } else { >- if (structure->hasMonoProto()) { >- JSValue prototype = structure->prototypeForLookup(state.m_globalObject); >- RELEASE_ASSERT(prototype.isObject()); >- jit.move(CCallHelpers::TrustedImmPtr(asObject(prototype)), baseForAccessGPR); >- } else { >- RELEASE_ASSERT(structure->isObject()); // Primitives must have a stored prototype. We use prototypeForLookup for them. >+ jit.move(scratch2GPR, valueGPR); >+ >+ CCallHelpers::Jump isInstance = jit.branchPtr(CCallHelpers::Equal, valueGPR, thisGPR); >+ > #if USE(JSVALUE64) >- jit.load64(MacroAssembler::Address(baseForAccessGPR, offsetRelativeToBase(knownPolyProtoOffset)), baseForAccessGPR); >- fallThrough.append(jit.branchIfNull(baseForAccessGPR)); >+ jit.branchIfCell(JSValueRegs(valueGPR)).linkTo(loop, &jit); > #else >- jit.load32(MacroAssembler::Address(baseForAccessGPR, offsetRelativeToBase(knownPolyProtoOffset) + PayloadOffset), baseForAccessGPR); >- fallThrough.append(jit.branchTestPtr(CCallHelpers::Zero, baseForAccessGPR)); >+ jit.branchTestPtr(CCallHelpers::NonZero, valueGPR).linkTo(loop, &jit); > #endif >- } >- } >- }); >- } else { >- if (viaProxy()) { >- fallThrough.append( >- jit.branchIfNotType(baseGPR, PureForwardingProxyType)); >- >- jit.loadPtr(CCallHelpers::Address(baseGPR, JSProxy::targetOffset()), scratchGPR); >- >- fallThrough.append( >- jit.branchStructure( >- CCallHelpers::NotEqual, >- CCallHelpers::Address(scratchGPR, JSCell::structureIDOffset()), >- structure())); >- } else { >- fallThrough.append( >- jit.branchStructure( >- CCallHelpers::NotEqual, >- CCallHelpers::Address(baseGPR, JSCell::structureIDOffset()), >- structure())); >- } >- } >+ >+ jit.boxBooleanPayload(false, valueGPR); >+ allocator.restoreReusedRegistersByPopping(jit, preservedState); >+ state.succeed(); >+ >+ isInstance.link(&jit); >+ jit.boxBooleanPayload(true, valueGPR); >+ allocator.restoreReusedRegistersByPopping(jit, preservedState); >+ state.succeed(); >+ >+ if (allocator.didReuseRegisters()) { >+ failAndIgnore.link(&jit); >+ allocator.restoreReusedRegistersByPopping(jit, preservedState); >+ state.failAndIgnore.append(jit.jump()); >+ } else >+ state.failAndIgnore.append(failAndIgnore); >+ return; >+ } >+ >+ default: >+ emitDefaultGuard(); > break; >- } }; >+ } > > generateImpl(state); > } >@@ -558,6 +689,12 @@ void AccessCase::generateImpl(AccessGene > state.succeed(); > return; > >+ case InstanceOfHit: >+ case InstanceOfMiss: >+ jit.boxBooleanPayload(m_type == InstanceOfHit, valueRegs.payloadGPR()); >+ state.succeed(); >+ return; >+ > case Load: > case GetGetter: > case Getter: >@@ -936,7 +1073,7 @@ void AccessCase::generateImpl(AccessGene > } > > ScratchRegisterAllocator::PreservedState preservedState = >- allocator.preserveReusedRegistersByPushing(jit, ScratchRegisterAllocator::ExtraStackSpace::SpaceForCCall); >+ allocator.preserveReusedRegistersByPushing(jit, ScratchRegisterAllocator::ExtraStackSpace::SpaceForCCall); > > CCallHelpers::JumpList slowPath; > >@@ -1112,6 +1249,7 @@ void AccessCase::generateImpl(AccessGene > case DirectArgumentsLength: > case ScopedArgumentsLength: > case ModuleNamespaceLoad: >+ case InstanceOfGeneric: > // These need to be handled by generateWithGuard(), since the guard is part of the > // algorithm. We can be sure that nobody will call generate() directly for these since they > // are not guarded by structure checks. >Index: Source/JavaScriptCore/bytecode/AccessCase.h >=================================================================== >--- Source/JavaScriptCore/bytecode/AccessCase.h (revision 231917) >+++ Source/JavaScriptCore/bytecode/AccessCase.h (working copy) >@@ -99,6 +99,9 @@ public: > DirectArgumentsLength, > ScopedArgumentsLength, > ModuleNamespaceLoad, >+ InstanceOfHit, >+ InstanceOfMiss, >+ InstanceOfGeneric > }; > > enum State : uint8_t { >@@ -126,7 +129,7 @@ public: > // This create method should be used for transitions. > static std::unique_ptr<AccessCase> create(VM&, JSCell* owner, PropertyOffset, Structure* oldStructure, > Structure* newStructure, const ObjectPropertyConditionSet&, std::unique_ptr<PolyProtoAccessChain>); >- >+ > static std::unique_ptr<AccessCase> fromStructureStubInfo(VM&, JSCell* owner, StructureStubInfo&); > > AccessType type() const { return m_type; } >@@ -193,7 +196,7 @@ public: > { > return !!m_polyProtoAccessChain; > } >- >+ > protected: > AccessCase(VM&, JSCell* owner, AccessType, PropertyOffset, Structure*, const ObjectPropertyConditionSet&, std::unique_ptr<PolyProtoAccessChain>); > AccessCase(AccessCase&&) = default; >Index: Source/JavaScriptCore/bytecode/InstanceOfAccessCase.cpp >=================================================================== >--- Source/JavaScriptCore/bytecode/InstanceOfAccessCase.cpp (nonexistent) >+++ Source/JavaScriptCore/bytecode/InstanceOfAccessCase.cpp (working copy) >@@ -0,0 +1,68 @@ >+/* >+ * Copyright (C) 2018 Apple Inc. All rights reserved. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above copyright >+ * notice, this list of conditions and the following disclaimer in the >+ * documentation and/or other materials provided with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY >+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE >+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR >+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR >+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, >+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, >+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR >+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY >+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT >+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE >+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+#include "config.h" >+#include "InstanceOfAccessCase.h" >+ >+#if ENABLE(JIT) >+ >+namespace JSC { >+ >+std::unique_ptr<AccessCase> InstanceOfAccessCase::create( >+ VM& vm, JSCell* owner, AccessType accessType, Structure* structure, >+ const ObjectPropertyConditionSet& conditionSet, JSObject* prototype) >+{ >+ return std::unique_ptr<AccessCase>(new InstanceOfAccessCase(vm, owner, accessType, structure, conditionSet, prototype)); >+} >+ >+void InstanceOfAccessCase::dumpImpl(PrintStream& out, CommaPrinter& comma) const >+{ >+ Base::dumpImpl(out, comma); >+ out.print(comma, "prototype = ", JSValue(prototype())); >+} >+ >+std::unique_ptr<AccessCase> InstanceOfAccessCase::clone() const >+{ >+ std::unique_ptr<InstanceOfAccessCase> result(new InstanceOfAccessCase(*this)); >+ result->resetState(); >+ return WTFMove(result); >+} >+ >+InstanceOfAccessCase::~InstanceOfAccessCase() >+{ >+} >+ >+InstanceOfAccessCase::InstanceOfAccessCase( >+ VM& vm, JSCell* owner, AccessType accessType, Structure* structure, >+ const ObjectPropertyConditionSet& conditionSet, JSObject* prototype) >+ : Base(vm, owner, accessType, invalidOffset, structure, conditionSet, nullptr) >+ , m_prototype(vm, owner, prototype) >+{ >+} >+ >+} // namespace JSC >+ >+#endif // ENABLE(JIT) >+ >Index: Source/JavaScriptCore/bytecode/InstanceOfAccessCase.h >=================================================================== >--- Source/JavaScriptCore/bytecode/InstanceOfAccessCase.h (nonexistent) >+++ Source/JavaScriptCore/bytecode/InstanceOfAccessCase.h (working copy) >@@ -0,0 +1,61 @@ >+/* >+ * Copyright (C) 2018 Apple Inc. All rights reserved. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above copyright >+ * notice, this list of conditions and the following disclaimer in the >+ * documentation and/or other materials provided with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY >+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE >+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR >+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR >+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, >+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, >+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR >+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY >+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT >+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE >+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+#pragma once >+ >+#if ENABLE(JIT) >+ >+#include "AccessCase.h" >+ >+namespace JSC { >+ >+class InstanceOfAccessCase : public AccessCase { >+public: >+ using Base = AccessCase; >+ >+ static std::unique_ptr<AccessCase> create( >+ VM&, JSCell*, AccessType, Structure*, const ObjectPropertyConditionSet&, >+ JSObject* prototype); >+ >+ JSObject* prototype() const { return m_prototype.get(); } >+ >+ void dumpImpl(PrintStream&, CommaPrinter&) const override; >+ std::unique_ptr<AccessCase> clone() const override; >+ >+ ~InstanceOfAccessCase(); >+ >+protected: >+ InstanceOfAccessCase( >+ VM&, JSCell*, AccessType, Structure*, const ObjectPropertyConditionSet&, >+ JSObject* prototype); >+ >+private: >+ WriteBarrier<JSObject> m_prototype; >+}; >+ >+} // namespace JSC >+ >+#endif // ENABLE(JIT) >+ >Index: Source/JavaScriptCore/bytecode/ObjectPropertyCondition.h >=================================================================== >--- Source/JavaScriptCore/bytecode/ObjectPropertyCondition.h (revision 231917) >+++ Source/JavaScriptCore/bytecode/ObjectPropertyCondition.h (working copy) >@@ -1,5 +1,5 @@ > /* >- * Copyright (C) 2015-2016 Apple Inc. All rights reserved. >+ * Copyright (C) 2015-2018 Apple Inc. All rights reserved. > * > * Redistribution and use in source and binary forms, with or without > * modification, are permitted provided that the following conditions >@@ -121,6 +121,22 @@ public: > vm.heap.writeBarrier(owner); > return equivalenceWithoutBarrier(object, uid, value); > } >+ >+ static ObjectPropertyCondition hasPrototypeWithoutBarrier(JSObject* object, JSObject* prototype) >+ { >+ ObjectPropertyCondition result; >+ result.m_object = object; >+ result.m_condition = PropertyCondition::hasPrototypeWithoutBarrier(prototype); >+ return result; >+ } >+ >+ static ObjectPropertyCondition hasPrototype( >+ VM& vm, JSCell* owner, JSObject* object, JSObject* prototype) >+ { >+ if (owner) >+ vm.heap.writeBarrier(owner); >+ return hasPrototypeWithoutBarrier(object, prototype); >+ } > > explicit operator bool() const { return !!m_condition; } > >Index: Source/JavaScriptCore/bytecode/ObjectPropertyConditionSet.cpp >=================================================================== >--- Source/JavaScriptCore/bytecode/ObjectPropertyConditionSet.cpp (revision 231917) >+++ Source/JavaScriptCore/bytecode/ObjectPropertyConditionSet.cpp (working copy) >@@ -1,5 +1,5 @@ > /* >- * Copyright (C) 2015 Apple Inc. All rights reserved. >+ * Copyright (C) 2015-2018 Apple Inc. All rights reserved. > * > * Redistribution and use in source and binary forms, with or without > * modification, are permitted provided that the following conditions >@@ -262,7 +262,7 @@ ObjectPropertyConditionSet generateCondi > dataLog("It's a proxy, so invalid.\n"); > return ObjectPropertyConditionSet::invalid(); > } >- >+ > if (structure->hasPolyProto()) { > // FIXME: Integrate this with PolyProtoAccessChain: > // https://bugs.webkit.org/show_bug.cgi?id=177339 >@@ -392,6 +392,36 @@ ObjectPropertyConditionSet generateCondi > }); > } > >+ObjectPropertyConditionSet generateConditionsForInstanceOf( >+ VM& vm, JSCell* owner, ExecState* exec, Structure* headStructure, JSObject* prototype, >+ bool shouldHit) >+{ >+ bool didHit = false; >+ if (ObjectPropertyConditionSetInternal::verbose) >+ dataLog("Searching for prototype ", JSValue(prototype), " starting with structure ", RawPointer(headStructure), " with shouldHit = ", shouldHit, "\n"); >+ ObjectPropertyConditionSet result = generateConditions( >+ vm, exec->lexicalGlobalObject(), headStructure, shouldHit ? prototype : nullptr, >+ [&] (Vector<ObjectPropertyCondition>& conditions, JSObject* object) -> bool { >+ if (ObjectPropertyConditionSetInternal::verbose) >+ dataLog("Encountered object: ", RawPointer(object), "\n"); >+ if (object == prototype) { >+ RELEASE_ASSERT(shouldHit); >+ didHit = true; >+ return true; >+ } >+ conditions.append( >+ ObjectPropertyCondition::hasPrototype( >+ vm, owner, object, object->structure()->storedPrototypeObject())); >+ return true; >+ }); >+ if (result.isValid()) { >+ if (ObjectPropertyConditionSetInternal::verbose) >+ dataLog("didHit = ", didHit, ", shouldHit = ", shouldHit, "\n"); >+ RELEASE_ASSERT(didHit == shouldHit); >+ } >+ return result; >+} >+ > ObjectPropertyConditionSet generateConditionsForPrototypeEquivalenceConcurrently( > VM& vm, JSGlobalObject* globalObject, Structure* headStructure, JSObject* prototype, UniquedStringImpl* uid) > { >Index: Source/JavaScriptCore/bytecode/ObjectPropertyConditionSet.h >=================================================================== >--- Source/JavaScriptCore/bytecode/ObjectPropertyConditionSet.h (revision 231917) >+++ Source/JavaScriptCore/bytecode/ObjectPropertyConditionSet.h (working copy) >@@ -1,5 +1,5 @@ > /* >- * Copyright (C) 2015 Apple Inc. All rights reserved. >+ * Copyright (C) 2015-2018 Apple Inc. All rights reserved. > * > * Redistribution and use in source and binary forms, with or without > * modification, are permitted provided that the following conditions >@@ -169,6 +169,9 @@ ObjectPropertyConditionSet generateCondi > VM&, JSCell* owner, ExecState*, Structure* headStructure, JSObject* prototype, > UniquedStringImpl* uid); > >+ObjectPropertyConditionSet generateConditionsForInstanceOf( >+ VM&, JSCell* owner, ExecState*, Structure* headStructure, JSObject* prototype, bool shouldHit); >+ > ObjectPropertyConditionSet generateConditionsForPrototypeEquivalenceConcurrently( > VM&, JSGlobalObject*, Structure* headStructure, JSObject* prototype, > UniquedStringImpl* uid); >Index: Source/JavaScriptCore/bytecode/PolymorphicAccess.cpp >=================================================================== >--- Source/JavaScriptCore/bytecode/PolymorphicAccess.cpp (revision 231917) >+++ Source/JavaScriptCore/bytecode/PolymorphicAccess.cpp (working copy) >@@ -248,7 +248,7 @@ AccessGenerationResult PolymorphicAccess > > casesToAdd.append(WTFMove(myCase)); > } >- >+ > if (PolymorphicAccessInternal::verbose) > dataLog("casesToAdd: ", listDump(casesToAdd), "\n"); > >@@ -258,39 +258,41 @@ AccessGenerationResult PolymorphicAccess > if (casesToAdd.isEmpty()) > return AccessGenerationResult::MadeNoChanges; > >- bool shouldReset = false; >- AccessGenerationResult resetResult(AccessGenerationResult::ResetStubAndFireWatchpoints); >- auto considerPolyProtoReset = [&] (Structure* a, Structure* b) { >- if (Structure::shouldConvertToPolyProto(a, b)) { >- // For now, we only reset if this is our first time invalidating this watchpoint. >- // The reason we don't immediately fire this watchpoint is that we may be already >- // watching the poly proto watchpoint, which if fired, would destroy us. We let >- // the person handling the result to do a delayed fire. >- ASSERT(a->rareData()->sharedPolyProtoWatchpoint().get() == b->rareData()->sharedPolyProtoWatchpoint().get()); >- if (a->rareData()->sharedPolyProtoWatchpoint()->isStillValid()) { >- shouldReset = true; >- resetResult.addWatchpointToFire(*a->rareData()->sharedPolyProtoWatchpoint(), StringFireDetail("Detected poly proto optimization opportunity.")); >+ if (stubInfo.accessType != AccessType::InstanceOf) { >+ bool shouldReset = false; >+ AccessGenerationResult resetResult(AccessGenerationResult::ResetStubAndFireWatchpoints); >+ auto considerPolyProtoReset = [&] (Structure* a, Structure* b) { >+ if (Structure::shouldConvertToPolyProto(a, b)) { >+ // For now, we only reset if this is our first time invalidating this watchpoint. >+ // The reason we don't immediately fire this watchpoint is that we may be already >+ // watching the poly proto watchpoint, which if fired, would destroy us. We let >+ // the person handling the result to do a delayed fire. >+ ASSERT(a->rareData()->sharedPolyProtoWatchpoint().get() == b->rareData()->sharedPolyProtoWatchpoint().get()); >+ if (a->rareData()->sharedPolyProtoWatchpoint()->isStillValid()) { >+ shouldReset = true; >+ resetResult.addWatchpointToFire(*a->rareData()->sharedPolyProtoWatchpoint(), StringFireDetail("Detected poly proto optimization opportunity.")); >+ } > } >- } >- }; >+ }; > >- for (auto& caseToAdd : casesToAdd) { >- for (auto& existingCase : m_list) { >- Structure* a = caseToAdd->structure(); >- Structure* b = existingCase->structure(); >- considerPolyProtoReset(a, b); >+ for (auto& caseToAdd : casesToAdd) { >+ for (auto& existingCase : m_list) { >+ Structure* a = caseToAdd->structure(); >+ Structure* b = existingCase->structure(); >+ considerPolyProtoReset(a, b); >+ } > } >- } >- for (unsigned i = 0; i < casesToAdd.size(); ++i) { >- for (unsigned j = i + 1; j < casesToAdd.size(); ++j) { >- Structure* a = casesToAdd[i]->structure(); >- Structure* b = casesToAdd[j]->structure(); >- considerPolyProtoReset(a, b); >+ for (unsigned i = 0; i < casesToAdd.size(); ++i) { >+ for (unsigned j = i + 1; j < casesToAdd.size(); ++j) { >+ Structure* a = casesToAdd[i]->structure(); >+ Structure* b = casesToAdd[j]->structure(); >+ considerPolyProtoReset(a, b); >+ } > } >- } > >- if (shouldReset) >- return resetResult; >+ if (shouldReset) >+ return resetResult; >+ } > > // Now add things to the new list. Note that at this point, we will still have old cases that > // may be replaced by the new ones. That's fine. We will sort that out when we regenerate. >@@ -421,7 +423,18 @@ AccessGenerationResult PolymorphicAccess > if (!someCase->couldStillSucceed()) > return; > >- // Figure out if this is replaced by any later case. >+ // Figure out if this is replaced by any later case. Given two cases A and B where A >+ // comes first in the case list, we know that A would have triggered first if we had >+ // generated the cases in a cascade. That's why this loop asks B->canReplace(A) but not >+ // A->canReplace(B). If A->canReplace(B) was true then A would never have requested >+ // repatching in cases where Repatch.cpp would have then gone on to generate B. If that >+ // did happen by some fluke, then we'd just miss the redundancy here, which wouldn't be >+ // incorrect - just slow. However, if A's checks failed and Repatch.cpp concluded that >+ // this new condition could be handled by B and B->canReplace(A), then this says that we >+ // don't need A anymore. >+ // >+ // If we can generate a binary switch, then A->canReplace(B) == B->canReplace(A). So, >+ // it doesn't matter that we only do the check in one direction. > for (unsigned j = srcIndex; j < m_list.size(); ++j) { > if (m_list[j]->canReplace(*someCase)) > return; >@@ -438,6 +451,18 @@ AccessGenerationResult PolymorphicAccess > } > m_list.resize(dstIndex); > >+ bool generatedFinalCode = false; >+ >+ // If the resulting set of cases is so big that we would stop caching and this is InstanceOf, >+ // then we want to generate the generic InstanceOf and then stop. >+ if (cases.size() >= Options::maxAccessVariantListSize() >+ && stubInfo.accessType == AccessType::InstanceOf) { >+ while (!cases.isEmpty()) >+ m_list.append(cases.takeLast()); >+ cases.append(AccessCase::create(vm, codeBlock, AccessCase::InstanceOfGeneric)); >+ generatedFinalCode = true; >+ } >+ > if (PolymorphicAccessInternal::verbose) > dataLog("Optimized cases: ", listDump(cases), "\n"); > >@@ -586,7 +611,7 @@ AccessGenerationResult PolymorphicAccess > m_list = WTFMove(cases); > > AccessGenerationResult::Kind resultKind; >- if (m_list.size() >= Options::maxAccessVariantListSize()) >+ if (m_list.size() >= Options::maxAccessVariantListSize() || generatedFinalCode) > resultKind = AccessGenerationResult::GeneratedFinalCode; > else > resultKind = AccessGenerationResult::GeneratedNewCode; >@@ -692,6 +717,15 @@ void printInternal(PrintStream& out, Acc > case AccessCase::ModuleNamespaceLoad: > out.print("ModuleNamespaceLoad"); > return; >+ case AccessCase::InstanceOfHit: >+ out.print("InstanceOfHit"); >+ return; >+ case AccessCase::InstanceOfMiss: >+ out.print("InstanceOfMiss"); >+ return; >+ case AccessCase::InstanceOfGeneric: >+ out.print("InstanceOfGeneric"); >+ return; > } > > RELEASE_ASSERT_NOT_REACHED(); >Index: Source/JavaScriptCore/bytecode/PropertyCondition.cpp >=================================================================== >--- Source/JavaScriptCore/bytecode/PropertyCondition.cpp (revision 231917) >+++ Source/JavaScriptCore/bytecode/PropertyCondition.cpp (working copy) >@@ -1,5 +1,5 @@ > /* >- * Copyright (C) 2015 Apple Inc. All rights reserved. >+ * Copyright (C) 2015-2018 Apple Inc. All rights reserved. > * > * Redistribution and use in source and binary forms, with or without > * modification, are permitted provided that the following conditions >@@ -43,17 +43,19 @@ void PropertyCondition::dumpInContext(Pr > return; > } > >- out.print(m_kind, " of ", m_uid); > switch (m_kind) { > case Presence: >- out.print(" at ", offset(), " with attributes ", attributes()); >+ out.print(m_kind, " of ", m_uid, " at ", offset(), " with attributes ", attributes()); > return; > case Absence: > case AbsenceOfSetEffect: >- out.print(" with prototype ", inContext(JSValue(prototype()), context)); >+ out.print(m_kind, " of ", m_uid, " with prototype ", inContext(JSValue(prototype()), context)); > return; > case Equivalence: >- out.print(" with ", inContext(requiredValue(), context)); >+ out.print(m_kind, " of ", m_uid, " with ", inContext(requiredValue(), context)); >+ return; >+ case HasPrototype: >+ out.print(m_kind, " with prototype ", inContext(JSValue(prototype()), context)); > return; > } > RELEASE_ASSERT_NOT_REACHED(); >@@ -78,11 +80,26 @@ bool PropertyCondition::isStillValidAssu > dataLog("Invalid because unset.\n"); > return false; > } >- >- if (!structure->propertyAccessesAreCacheable()) { >- if (PropertyConditionInternal::verbose) >- dataLog("Invalid because accesses are not cacheable.\n"); >- return false; >+ >+ switch (m_kind) { >+ case Presence: >+ case Absence: >+ case AbsenceOfSetEffect: >+ case Equivalence: >+ if (!structure->propertyAccessesAreCacheable()) { >+ if (PropertyConditionInternal::verbose) >+ dataLog("Invalid because property accesses are not cacheable.\n"); >+ return false; >+ } >+ break; >+ >+ case HasPrototype: >+ if (!structure->prototypeQueriesAreCacheable()) { >+ if (PropertyConditionInternal::verbose) >+ dataLog("Invalid because prototype queries are not cacheable.\n"); >+ return false; >+ } >+ break; > } > > switch (m_kind) { >@@ -174,6 +191,33 @@ bool PropertyCondition::isStillValidAssu > return true; > } > >+ case HasPrototype: { >+ if (structure->isDictionary()) { >+ if (PropertyConditionInternal::verbose) >+ dataLog("Invalid because it's a dictionary.\n"); >+ return false; >+ } >+ >+ if (structure->hasPolyProto()) { >+ // FIXME: I think this is too conservative. We can probably prove this if >+ // we have the base. Anyways, we should make this work when integrating >+ // OPC and poly proto. >+ // https://bugs.webkit.org/show_bug.cgi?id=177339 >+ return false; >+ } >+ >+ if (structure->storedPrototypeObject() != prototype()) { >+ if (PropertyConditionInternal::verbose) { >+ dataLog( >+ "Invalid because the prototype is ", structure->storedPrototype(), " even though " >+ "it should have been ", JSValue(prototype()), "\n"); >+ } >+ return false; >+ } >+ >+ return true; >+ } >+ > case Equivalence: { > if (!base || base->structure() != structure) { > // Conservatively return false, since we cannot verify this one without having the >@@ -226,9 +270,13 @@ bool PropertyCondition::validityRequires > case Absence: > case Equivalence: > return structure->needImpurePropertyWatchpoint(); >- default: >+ case AbsenceOfSetEffect: >+ case HasPrototype: > return false; > } >+ >+ RELEASE_ASSERT_NOT_REACHED(); >+ return false; > } > > bool PropertyCondition::isStillValid(Structure* structure, JSObject* base) const >@@ -375,6 +423,9 @@ void printInternal(PrintStream& out, JSC > case JSC::PropertyCondition::Equivalence: > out.print("Equivalence"); > return; >+ case JSC::PropertyCondition::HasPrototype: >+ out.print("HasPrototype"); >+ return; > } > RELEASE_ASSERT_NOT_REACHED(); > } >Index: Source/JavaScriptCore/bytecode/PropertyCondition.h >=================================================================== >--- Source/JavaScriptCore/bytecode/PropertyCondition.h (revision 231917) >+++ Source/JavaScriptCore/bytecode/PropertyCondition.h (working copy) >@@ -1,5 +1,5 @@ > /* >- * Copyright (C) 2015-2016 Apple Inc. All rights reserved. >+ * Copyright (C) 2015-2018 Apple Inc. All rights reserved. > * > * Redistribution and use in source and binary forms, with or without > * modification, are permitted provided that the following conditions >@@ -38,7 +38,8 @@ public: > Presence, > Absence, > AbsenceOfSetEffect, >- Equivalence // An adaptive watchpoint on this will be a pair of watchpoints, and when the structure transitions, we will set the replacement watchpoint on the new structure. >+ Equivalence, // An adaptive watchpoint on this will be a pair of watchpoints, and when the structure transitions, we will set the replacement watchpoint on the new structure. >+ HasPrototype > }; > > PropertyCondition() >@@ -77,7 +78,7 @@ public: > PropertyCondition result; > result.m_uid = uid; > result.m_kind = Absence; >- result.u.absence.prototype = prototype; >+ result.u.prototype.prototype = prototype; > return result; > } > >@@ -95,7 +96,7 @@ public: > PropertyCondition result; > result.m_uid = uid; > result.m_kind = AbsenceOfSetEffect; >- result.u.absence.prototype = prototype; >+ result.u.prototype.prototype = prototype; > return result; > } > >@@ -125,6 +126,21 @@ public: > return equivalenceWithoutBarrier(uid, value); > } > >+ static PropertyCondition hasPrototypeWithoutBarrier(JSObject* prototype) >+ { >+ PropertyCondition result; >+ result.m_kind = HasPrototype; >+ result.u.prototype.prototype = prototype; >+ return result; >+ } >+ >+ static PropertyCondition hasPrototype(VM& vm, JSCell* owner, JSObject* prototype) >+ { >+ if (owner) >+ vm.heap.writeBarrier(owner); >+ return hasPrototypeWithoutBarrier(prototype); >+ } >+ > explicit operator bool() const { return m_uid || m_kind != Presence; } > > Kind kind() const { return m_kind; } >@@ -143,11 +159,15 @@ public: > return u.presence.attributes; > } > >- bool hasPrototype() const { return !!*this && (m_kind == Absence || m_kind == AbsenceOfSetEffect); } >+ bool hasPrototype() const >+ { >+ return !!*this >+ && (m_kind == Absence || m_kind == AbsenceOfSetEffect || m_kind == HasPrototype); >+ } > JSObject* prototype() const > { > ASSERT(hasPrototype()); >- return u.absence.prototype; >+ return u.prototype.prototype; > } > > bool hasRequiredValue() const { return !!*this && m_kind == Equivalence; } >@@ -170,7 +190,8 @@ public: > break; > case Absence: > case AbsenceOfSetEffect: >- result ^= WTF::PtrHash<JSObject*>::hash(u.absence.prototype); >+ case HasPrototype: >+ result ^= WTF::PtrHash<JSObject*>::hash(u.prototype.prototype); > break; > case Equivalence: > result ^= EncodedJSValueHash::hash(u.equivalence.value); >@@ -191,7 +212,8 @@ public: > && u.presence.attributes == other.u.presence.attributes; > case Absence: > case AbsenceOfSetEffect: >- return u.absence.prototype == other.u.absence.prototype; >+ case HasPrototype: >+ return u.prototype.prototype == other.u.prototype.prototype; > case Equivalence: > return u.equivalence.value == other.u.equivalence.value; > } >@@ -300,7 +322,7 @@ private: > } presence; > struct { > JSObject* prototype; >- } absence; >+ } prototype; > struct { > EncodedJSValue value; > } equivalence; >Index: Source/JavaScriptCore/bytecode/StructureStubInfo.cpp >=================================================================== >--- Source/JavaScriptCore/bytecode/StructureStubInfo.cpp (revision 231917) >+++ Source/JavaScriptCore/bytecode/StructureStubInfo.cpp (working copy) >@@ -50,6 +50,7 @@ StructureStubInfo::StructureStubInfo(Acc > , resetByGC(false) > , tookSlowPath(false) > , everConsidered(false) >+ , prototypeIsKnownObject(false) > { > } > >@@ -235,7 +236,10 @@ void StructureStubInfo::reset(CodeBlock* > resetPutByID(codeBlock, *this); > break; > case AccessType::In: >- resetIn(codeBlock, *this); >+ resetIn(*this); >+ break; >+ case AccessType::InstanceOf: >+ resetInstanceOf(*this); > break; > } > >Index: Source/JavaScriptCore/bytecode/StructureStubInfo.h >=================================================================== >--- Source/JavaScriptCore/bytecode/StructureStubInfo.h (revision 231917) >+++ Source/JavaScriptCore/bytecode/StructureStubInfo.h (working copy) >@@ -50,7 +50,8 @@ enum class AccessType : int8_t { > GetDirect, > TryGet, > Put, >- In >+ In, >+ InstanceOf > }; > > enum class CacheType : int8_t { >@@ -138,6 +139,10 @@ public: > // we don't already have a case buffered for. Note that if this returns true but the > // bufferingCountdown is not zero then we will buffer the access case for later without > // immediately generating code for it. >+ // >+ // NOTE: This will behave oddly for InstanceOf if the user varies the prototype but not >+ // the base's structure. That seems unlikely for the canonical use of instanceof, where >+ // the prototype is fixed. > bool isNewlyAdded = bufferedStructures.add(structure); > if (isNewlyAdded) { > VM& vm = *codeBlock->vm(); >@@ -169,7 +174,7 @@ public: > StructureSet bufferedStructures; > > struct { >- CodeLocationLabel<JITStubRoutinePtrTag> start; // This is either the start of the inline IC for *byId caches, or the location of patchable jump for 'in' caches. >+ CodeLocationLabel<JITStubRoutinePtrTag> start; // This is either the start of the inline IC for *byId caches, or the location of patchable jump for 'in' and 'instanceof' caches. > RegisterSet usedRegisters; > uint32_t inlineSize; > int32_t deltaFromStartToSlowPathCallLocation; >@@ -188,9 +193,9 @@ public: > CodeLocationCall<JSInternalPtrTag> slowPathCallLocation() { return patch.start.callAtOffset<JSInternalPtrTag>(patch.deltaFromStartToSlowPathCallLocation); } > CodeLocationLabel<JSInternalPtrTag> doneLocation() { return patch.start.labelAtOffset<JSInternalPtrTag>(patch.inlineSize); } > CodeLocationLabel<JITStubRoutinePtrTag> slowPathStartLocation() { return patch.start.labelAtOffset(patch.deltaFromStartToSlowPathStart); } >- CodeLocationJump<JSInternalPtrTag> patchableJumpForIn() >+ CodeLocationJump<JSInternalPtrTag> patchableJump() > { >- ASSERT(accessType == AccessType::In); >+ ASSERT(accessType == AccessType::In || accessType == AccessType::InstanceOf); > return patch.start.jumpAtOffset<JSInternalPtrTag>(0); > } > >@@ -213,6 +218,7 @@ public: > bool resetByGC : 1; > bool tookSlowPath : 1; > bool everConsidered : 1; >+ bool prototypeIsKnownObject : 1; // Only relevant for InstanceOf. > }; > > inline CodeOrigin getStructureStubInfoCodeOrigin(StructureStubInfo& structureStubInfo) >Index: Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp (revision 231917) >+++ Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp (working copy) >@@ -4783,6 +4783,9 @@ void ByteCodeParser::parseBlock(unsigned > } > > case op_instanceof: { >+ // FIXME: We should turn this into either a CheckStructure followed by a constant, or >+ // some kind of MultiInstanceOf thing, if the InstanceOf IC is stable. >+ // https://bugs.webkit.org/show_bug.cgi?id=185695 > auto& bytecode = *reinterpret_cast<OpInstanceof*>(currentInstruction); > Node* value = get(VirtualRegister(bytecode.value())); > Node* prototype = get(VirtualRegister(bytecode.prototype())); >Index: Source/JavaScriptCore/dfg/DFGFixupPhase.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGFixupPhase.cpp (revision 231917) >+++ Source/JavaScriptCore/dfg/DFGFixupPhase.cpp (working copy) >@@ -1455,9 +1455,13 @@ private: > } > > case InstanceOf: { >- if (!(node->child1()->prediction() & ~SpecCell)) >+ if (node->child1()->shouldSpeculateCell() >+ && node->child2()->shouldSpeculateCell() >+ && is64Bit()) { > fixEdge<CellUse>(node->child1()); >- fixEdge<CellUse>(node->child2()); >+ fixEdge<CellUse>(node->child2()); >+ break; >+ } > break; > } > >Index: Source/JavaScriptCore/dfg/DFGInlineCacheWrapper.h >=================================================================== >--- Source/JavaScriptCore/dfg/DFGInlineCacheWrapper.h (revision 231917) >+++ Source/JavaScriptCore/dfg/DFGInlineCacheWrapper.h (working copy) >@@ -1,5 +1,5 @@ > /* >- * Copyright (C) 2013 Apple Inc. All rights reserved. >+ * Copyright (C) 2013-2018 Apple Inc. All rights reserved. > * > * Redistribution and use in source and binary forms, with or without > * modification, are permitted provided that the following conditions >@@ -43,7 +43,7 @@ struct InlineCacheWrapper { > { > } > >- void finalize(LinkBuffer&); >+ void finalize(LinkBuffer&, LinkBuffer&); > > GeneratorType m_generator; > SlowPathGenerator* m_slowPath; >Index: Source/JavaScriptCore/dfg/DFGInlineCacheWrapperInlines.h >=================================================================== >--- Source/JavaScriptCore/dfg/DFGInlineCacheWrapperInlines.h (revision 231917) >+++ Source/JavaScriptCore/dfg/DFGInlineCacheWrapperInlines.h (working copy) >@@ -1,5 +1,5 @@ > /* >- * Copyright (C) 2013 Apple Inc. All rights reserved. >+ * Copyright (C) 2013-2018 Apple Inc. All rights reserved. > * > * Redistribution and use in source and binary forms, with or without > * modification, are permitted provided that the following conditions >@@ -33,10 +33,10 @@ > namespace JSC { namespace DFG { > > template<typename GeneratorType> >-void InlineCacheWrapper<GeneratorType>::finalize(LinkBuffer& linkBuffer) >+void InlineCacheWrapper<GeneratorType>::finalize(LinkBuffer& fastPath, LinkBuffer& slowPath) > { > m_generator.reportSlowPathCall(m_slowPath->label(), m_slowPath->call()); >- m_generator.finalize(linkBuffer); >+ m_generator.finalize(fastPath, slowPath); > } > > } } // namespace JSC::DFG >Index: Source/JavaScriptCore/dfg/DFGJITCompiler.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGJITCompiler.cpp (revision 231917) >+++ Source/JavaScriptCore/dfg/DFGJITCompiler.cpp (working copy) >@@ -262,12 +262,10 @@ void JITCompiler::link(LinkBuffer& linkB > for (unsigned i = 0; i < m_calls.size(); ++i) > linkBuffer.link(m_calls[i].m_call, m_calls[i].m_function); > >- for (unsigned i = m_getByIds.size(); i--;) >- m_getByIds[i].finalize(linkBuffer); >- for (unsigned i = m_getByIdsWithThis.size(); i--;) >- m_getByIdsWithThis[i].finalize(linkBuffer); >- for (unsigned i = m_putByIds.size(); i--;) >- m_putByIds[i].finalize(linkBuffer); >+ finalizeInlineCaches(m_getByIds, linkBuffer); >+ finalizeInlineCaches(m_getByIdsWithThis, linkBuffer); >+ finalizeInlineCaches(m_putByIds, linkBuffer); >+ finalizeInlineCaches(m_instanceOfs, linkBuffer); > > for (unsigned i = 0; i < m_ins.size(); ++i) { > StructureStubInfo& info = *m_ins[i].m_stubInfo; >Index: Source/JavaScriptCore/dfg/DFGJITCompiler.h >=================================================================== >--- Source/JavaScriptCore/dfg/DFGJITCompiler.h (revision 231917) >+++ Source/JavaScriptCore/dfg/DFGJITCompiler.h (working copy) >@@ -203,6 +203,11 @@ public: > { > m_putByIds.append(InlineCacheWrapper<JITPutByIdGenerator>(gen, slowPath)); > } >+ >+ void addInstanceOf(const JITInstanceOfGenerator& gen, SlowPathGenerator* slowPath) >+ { >+ m_instanceOfs.append(InlineCacheWrapper<JITInstanceOfGenerator>(gen, slowPath)); >+ } > > void addIn(const InRecord& record) > { >@@ -354,6 +359,7 @@ private: > Vector<InlineCacheWrapper<JITGetByIdGenerator>, 4> m_getByIds; > Vector<InlineCacheWrapper<JITGetByIdWithThisGenerator>, 4> m_getByIdsWithThis; > Vector<InlineCacheWrapper<JITPutByIdGenerator>, 4> m_putByIds; >+ Vector<InlineCacheWrapper<JITInstanceOfGenerator>, 4> m_instanceOfs; > Vector<InRecord, 4> m_ins; > Vector<JSCallRecord, 4> m_jsCalls; > Vector<JSDirectCallRecord, 4> m_jsDirectCalls; >Index: Source/JavaScriptCore/dfg/DFGOperations.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGOperations.cpp (revision 231917) >+++ Source/JavaScriptCore/dfg/DFGOperations.cpp (working copy) >@@ -2423,15 +2423,6 @@ int64_t JIT_OPERATION operationConvertDo > return tryConvertToInt52(value); > } > >-size_t JIT_OPERATION operationDefaultHasInstance(ExecState* exec, JSCell* value, JSCell* proto) // Returns jsBoolean(True|False) on 64-bit. >-{ >- VM* vm = &exec->vm(); >- NativeCallFrameTracer tracer(vm, exec); >- if (JSObject::defaultHasInstance(exec, value, proto)) >- return 1; >- return 0; >-} >- > char* JIT_OPERATION operationNewRawObject(ExecState* exec, Structure* structure, int32_t length, Butterfly* butterfly) > { > VM& vm = exec->vm(); >Index: Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp (revision 231917) >+++ Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp (working copy) >@@ -174,11 +174,10 @@ void SpeculativeJIT::cachedGetById(CodeO > gen.generateFastPath(m_jit); > > JITCompiler::JumpList slowCases; >- if (slowPathTarget.isSet()) >- slowCases.append(slowPathTarget); >+ slowCases.append(slowPathTarget); > slowCases.append(gen.slowPathJump()); > >- auto slowPath = slowPathCall( >+ std::unique_ptr<SlowPathGenerator> slowPath = slowPathCall( > slowCases, this, appropriateOptimizingGetByIdFunction(type), > spillMode, ExceptionCheckRequirement::CheckNeeded, > resultGPR, gen.stubInfo(), baseGPR, identifierUID(identifierNumber)); >@@ -202,11 +201,10 @@ void SpeculativeJIT::cachedGetByIdWithTh > gen.generateFastPath(m_jit); > > JITCompiler::JumpList slowCases; >- if (!slowPathTarget.empty()) >- slowCases.append(slowPathTarget); >+ slowCases.append(slowPathTarget); > slowCases.append(gen.slowPathJump()); > >- auto slowPath = slowPathCall( >+ std::unique_ptr<SlowPathGenerator> slowPath = slowPathCall( > slowCases, this, operationGetByIdWithThisOptimize, > DontSpill, ExceptionCheckRequirement::CheckNeeded, > resultGPR, gen.stubInfo(), baseGPR, thisGPR, identifierUID(identifierNumber)); >Index: Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp (revision 231917) >+++ Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp (working copy) >@@ -373,6 +373,9 @@ RegisterSet SpeculativeJIT::usedRegister > result.set(fpr); > } > >+ // FIXME: This is overly conservative. We could subtract out those callee-saves that we >+ // actually saved. >+ // https://bugs.webkit.org/show_bug.cgi?id=185686 > result.merge(RegisterSet::stubUnavailableRegisters()); > > return result; >@@ -3317,69 +3320,6 @@ void SpeculativeJIT::compileGetByValWith > jsValueResult(resultRegs, node); > } > >-void SpeculativeJIT::compileInstanceOfForObject(Node*, GPRReg valueReg, GPRReg prototypeReg, GPRReg scratchReg, GPRReg scratch2Reg, GPRReg scratch3Reg) >-{ >- // Check that prototype is an object. >- speculationCheck(BadType, JSValueRegs(), 0, m_jit.branchIfNotObject(prototypeReg)); >- >- // Initialize scratchReg with the value being checked. >- m_jit.move(valueReg, scratchReg); >- >- // Walk up the prototype chain of the value (in scratchReg), comparing to prototypeReg. >- MacroAssembler::Label loop(&m_jit); >- MacroAssembler::Jump performDefaultHasInstance = m_jit.branchIfType(scratchReg, ProxyObjectType); >- m_jit.emitLoadStructure(*m_jit.vm(), scratchReg, scratch3Reg, scratch2Reg); >-#if USE(JSVALUE64) >- m_jit.load64(MacroAssembler::Address(scratch3Reg, Structure::prototypeOffset()), scratch3Reg); >- auto hasMonoProto = m_jit.branchIfNotEmpty(scratch3Reg); >- m_jit.load64(JITCompiler::Address(scratchReg, offsetRelativeToBase(knownPolyProtoOffset)), scratch3Reg); >- hasMonoProto.link(&m_jit); >- m_jit.move(scratch3Reg, scratchReg); >-#else >- m_jit.load32(MacroAssembler::Address(scratch3Reg, Structure::prototypeOffset() + TagOffset), scratch2Reg); >- m_jit.load32(MacroAssembler::Address(scratch3Reg, Structure::prototypeOffset() + PayloadOffset), scratch3Reg); >- auto hasMonoProto = m_jit.branchIfNotEmpty(scratch2Reg); >- m_jit.load32(JITCompiler::Address(scratchReg, offsetRelativeToBase(knownPolyProtoOffset) + PayloadOffset), scratch3Reg); >- hasMonoProto.link(&m_jit); >- m_jit.move(scratch3Reg, scratchReg); >-#endif >- >- MacroAssembler::Jump isInstance = m_jit.branchPtr(MacroAssembler::Equal, scratchReg, prototypeReg); >-#if USE(JSVALUE64) >- m_jit.branchIfCell(JSValueRegs(scratchReg)).linkTo(loop, &m_jit); >-#else >- m_jit.branchTestPtr(MacroAssembler::NonZero, scratchReg).linkTo(loop, &m_jit); >-#endif >- >- // No match - result is false. >-#if USE(JSVALUE64) >- m_jit.move(MacroAssembler::TrustedImm64(JSValue::encode(jsBoolean(false))), scratchReg); >-#else >- m_jit.move(MacroAssembler::TrustedImm32(0), scratchReg); >-#endif >- MacroAssembler::JumpList doneJumps; >- doneJumps.append(m_jit.jump()); >- >- performDefaultHasInstance.link(&m_jit); >- silentSpillAllRegisters(scratchReg); >- callOperation(operationDefaultHasInstance, scratchReg, valueReg, prototypeReg); >- silentFillAllRegisters(); >- m_jit.exceptionCheck(); >-#if USE(JSVALUE64) >- m_jit.or32(TrustedImm32(ValueFalse), scratchReg); >-#endif >- doneJumps.append(m_jit.jump()); >- >- isInstance.link(&m_jit); >-#if USE(JSVALUE64) >- m_jit.move(MacroAssembler::TrustedImm64(JSValue::encode(jsBoolean(true))), scratchReg); >-#else >- m_jit.move(MacroAssembler::TrustedImm32(1), scratchReg); >-#endif >- >- doneJumps.link(&m_jit); >-} >- > void SpeculativeJIT::compileCheckTypeInfoFlags(Node* node) > { > SpeculateCellOperand base(this, node->child1()); >@@ -3490,56 +3430,83 @@ void SpeculativeJIT::compileOverridesHas > unblessedBooleanResult(resultGPR, node); > } > >+void SpeculativeJIT::compileInstanceOfForCells(Node* node, JSValueRegs valueRegs, JSValueRegs prototypeRegs, GPRReg resultGPR, GPRReg scratchGPR, GPRReg scratch2GPR, JITCompiler::Jump slowCase) >+{ >+ CallSiteIndex callSiteIndex = >+ m_jit.recordCallSiteAndGenerateExceptionHandlingOSRExitIfNeeded( >+ node->origin.semantic, m_stream->size()); >+ >+ JITInstanceOfGenerator gen( >+ m_jit.codeBlock(), node->origin.semantic, callSiteIndex, usedRegisters(), resultGPR, >+ valueRegs.payloadGPR(), prototypeRegs.payloadGPR(), scratchGPR, scratch2GPR, >+ m_state.forNode(node->child2()).isType(SpecObject | ~SpecCell)); >+ gen.generateFastPath(m_jit); >+ >+ JITCompiler::JumpList slowCases; >+ slowCases.append(slowCase); >+ >+ std::unique_ptr<SlowPathGenerator> slowPath = slowPathCall( >+ slowCases, this, operationInstanceOfOptimize, resultGPR, gen.stubInfo(), valueRegs, >+ prototypeRegs); >+ >+ m_jit.addInstanceOf(gen, slowPath.get()); >+ addSlowPathGenerator(WTFMove(slowPath)); >+} >+ > void SpeculativeJIT::compileInstanceOf(Node* node) > { >- if (node->child1().useKind() == UntypedUse) { >- // It might not be a cell. Speculate less aggressively. >- // Or: it might only be used once (i.e. by us), so we get zero benefit >- // from speculating any more aggressively than we absolutely need to. >- >- JSValueOperand value(this, node->child1()); >+#if USE(JSVALUE64) >+ if (node->child1().useKind() == CellUse >+ && node->child2().useKind() == CellUse) { >+ SpeculateCellOperand value(this, node->child1()); > SpeculateCellOperand prototype(this, node->child2()); >+ >+ GPRTemporary result(this); > GPRTemporary scratch(this); > GPRTemporary scratch2(this); >- GPRTemporary scratch3(this); >- >- GPRReg prototypeReg = prototype.gpr(); >- GPRReg scratchReg = scratch.gpr(); >- GPRReg scratch2Reg = scratch2.gpr(); >- GPRReg scratch3Reg = scratch3.gpr(); >- >- MacroAssembler::Jump isCell = m_jit.branchIfCell(value.jsValueRegs()); >- GPRReg valueReg = value.jsValueRegs().payloadGPR(); >- moveFalseTo(scratchReg); >- >- MacroAssembler::Jump done = m_jit.jump(); > >- isCell.link(&m_jit); >+ GPRReg valueGPR = value.gpr(); >+ GPRReg prototypeGPR = prototype.gpr(); >+ GPRReg resultGPR = result.gpr(); >+ GPRReg scratchGPR = scratch.gpr(); >+ GPRReg scratch2GPR = scratch2.gpr(); > >- compileInstanceOfForObject(node, valueReg, prototypeReg, scratchReg, scratch2Reg, scratch3Reg); >+ compileInstanceOfForCells(node, JSValueRegs(valueGPR), JSValueRegs(prototypeGPR), resultGPR, scratchGPR, scratch2GPR); > >- done.link(&m_jit); >- >- blessedBooleanResult(scratchReg, node); >+ blessedBooleanResult(resultGPR, node); > return; > } >+#endif > >- SpeculateCellOperand value(this, node->child1()); >- SpeculateCellOperand prototype(this, node->child2()); >+ DFG_ASSERT(m_jit.graph(), node, node->child1().useKind() == UntypedUse); >+ DFG_ASSERT(m_jit.graph(), node, node->child2().useKind() == UntypedUse); > >+ JSValueOperand value(this, node->child1()); >+ JSValueOperand prototype(this, node->child2()); >+ >+ GPRTemporary result(this); > GPRTemporary scratch(this); >- GPRTemporary scratch2(this); >- GPRTemporary scratch3(this); > >- GPRReg valueReg = value.gpr(); >- GPRReg prototypeReg = prototype.gpr(); >- GPRReg scratchReg = scratch.gpr(); >- GPRReg scratch2Reg = scratch2.gpr(); >- GPRReg scratch3Reg = scratch3.gpr(); >+ JSValueRegs valueRegs = value.jsValueRegs(); >+ JSValueRegs prototypeRegs = prototype.jsValueRegs(); > >- compileInstanceOfForObject(node, valueReg, prototypeReg, scratchReg, scratch2Reg, scratch3Reg); >- >- blessedBooleanResult(scratchReg, node); >+ GPRReg resultGPR = result.gpr(); >+ GPRReg scratchGPR = scratch.gpr(); >+ >+ JITCompiler::Jump isCell = m_jit.branchIfCell(valueRegs); >+ moveFalseTo(resultGPR); >+ >+ JITCompiler::Jump done = m_jit.jump(); >+ >+ isCell.link(&m_jit); >+ >+ JITCompiler::Jump slowCase = m_jit.branchIfNotCell(prototypeRegs); >+ >+ compileInstanceOfForCells(node, valueRegs, prototypeRegs, resultGPR, scratchGPR, InvalidGPRReg, slowCase); >+ >+ done.link(&m_jit); >+ blessedBooleanResult(resultGPR, node); >+ return; > } > > template<typename SnippetGenerator, J_JITOperation_EJJ snippetSlowPathFunction> >Index: Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h >=================================================================== >--- Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h (revision 231917) >+++ Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h (working copy) >@@ -747,7 +747,7 @@ public: > void nonSpeculativeNonPeepholeStrictEq(Node*, bool invert = false); > bool nonSpeculativeStrictEq(Node*, bool invert = false); > >- void compileInstanceOfForObject(Node*, GPRReg valueReg, GPRReg prototypeReg, GPRReg scratchAndResultReg, GPRReg scratch2Reg, GPRReg scratch3Reg); >+ void compileInstanceOfForCells(Node*, JSValueRegs valueGPR, JSValueRegs prototypeGPR, GPRReg resultGPT, GPRReg scratchGPR, GPRReg scratch2GPR, JITCompiler::Jump slowCase = JITCompiler::Jump()); > void compileInstanceOf(Node*); > void compileInstanceOfCustom(Node*); > void compileOverridesHasInstance(Node*); >Index: Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp >=================================================================== >--- Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp (revision 231917) >+++ Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp (working copy) >@@ -2947,7 +2947,7 @@ private: > if (!validationEnabled()) > return; > >- B3::PatchpointValue* patchpoint = m_out.patchpoint(Void); >+ PatchpointValue* patchpoint = m_out.patchpoint(Void); > patchpoint->appendSomeRegister(lowJSValue(m_node->child1())); > patchpoint->setGenerator( > [=] (CCallHelpers& jit, const StackmapGenerationParams& params) { >@@ -3401,7 +3401,7 @@ private: > LValue value = lowJSValue(node->child2()); > auto uid = m_graph.identifiers()[node->identifierNumber()]; > >- B3::PatchpointValue* patchpoint = m_out.patchpoint(Void); >+ PatchpointValue* patchpoint = m_out.patchpoint(Void); > patchpoint->appendSomeRegister(base); > patchpoint->appendSomeRegister(value); > patchpoint->append(m_tagMask, ValueRep::reg(GPRInfo::tagMaskRegister)); >@@ -3456,7 +3456,7 @@ private: > > jit.addLinkTask( > [=] (LinkBuffer& linkBuffer) { >- generator->finalize(linkBuffer); >+ generator->finalize(linkBuffer, linkBuffer); > }); > }); > }); >@@ -8955,7 +8955,7 @@ private: > unsure(continuation), unsure(doubleNotNanOrInf)); > > m_out.appendTo(doubleNotNanOrInf, continuation); >- B3::PatchpointValue* patchpoint = m_out.patchpoint(Int32); >+ PatchpointValue* patchpoint = m_out.patchpoint(Int32); > patchpoint->appendSomeRegister(asDouble); > patchpoint->numFPScratchRegisters = 1; > patchpoint->effects = Effects::none(); >@@ -9679,7 +9679,7 @@ private: > if (JSString* string = node->child2()->dynamicCastConstant<JSString*>(vm())) { > if (string->tryGetValueImpl() && string->tryGetValueImpl()->isAtomic()) { > UniquedStringImpl* str = bitwise_cast<UniquedStringImpl*>(string->tryGetValueImpl()); >- B3::PatchpointValue* patchpoint = m_out.patchpoint(Int64); >+ PatchpointValue* patchpoint = m_out.patchpoint(Int64); > patchpoint->appendSomeRegister(cell); > patchpoint->append(m_tagMask, ValueRep::lateReg(GPRInfo::tagMaskRegister)); > patchpoint->append(m_tagTypeNumber, ValueRep::lateReg(GPRInfo::tagTypeNumberRegister)); >@@ -9910,82 +9910,110 @@ private: > > void compileInstanceOf() > { >- LValue cell; >- >- if (m_node->child1().useKind() == UntypedUse) >- cell = lowJSValue(m_node->child1()); >- else >- cell = lowCell(m_node->child1()); >- >- LValue prototype = lowCell(m_node->child2()); >- >- LBasicBlock isCellCase = m_out.newBlock(); >- LBasicBlock loop = m_out.newBlock(); >- LBasicBlock loadPrototypeBits = m_out.newBlock(); >- LBasicBlock loadPolyProto = m_out.newBlock(); >- LBasicBlock comparePrototype = m_out.newBlock(); >- LBasicBlock notYetInstance = m_out.newBlock(); >- LBasicBlock defaultHasInstanceSlow = m_out.newBlock(); >- LBasicBlock continuation = m_out.newBlock(); >- >- LValue condition; >- if (m_node->child1().useKind() == UntypedUse) >- condition = isCell(cell, provenType(m_node->child1())); >- else >- condition = m_out.booleanTrue; >- >- ValueFromBlock notCellResult = m_out.anchor(m_out.booleanFalse); >- m_out.branch(condition, unsure(isCellCase), unsure(continuation)); >+ Node* node = m_node; >+ State* state = &m_ftlState; > >- LBasicBlock lastNext = m_out.appendTo(isCellCase, loop); >+ LValue value; >+ LValue prototype; >+ bool valueIsCell; >+ bool prototypeIsCell; >+ if (m_node->child1().useKind() == CellUse >+ && m_node->child2().useKind() == CellUse) { >+ value = lowCell(m_node->child1()); >+ prototype = lowCell(m_node->child2()); >+ >+ valueIsCell = true; >+ prototypeIsCell = true; >+ } else { >+ DFG_ASSERT(m_graph, m_node, m_node->child1().useKind() == UntypedUse); >+ DFG_ASSERT(m_graph, m_node, m_node->child2().useKind() == UntypedUse); >+ >+ value = lowJSValue(m_node->child1()); >+ prototype = lowJSValue(m_node->child2()); >+ >+ valueIsCell = abstractValue(m_node->child1()).isType(SpecCell); >+ prototypeIsCell = abstractValue(m_node->child2()).isType(SpecCell); >+ } > >- speculate(BadType, noValue(), 0, isNotObject(prototype, provenType(m_node->child2()))); >+ bool prototypeIsObject = abstractValue(m_node->child2()).isType(SpecObject | ~SpecCell); > >- ValueFromBlock originalValue = m_out.anchor(cell); >- m_out.jump(loop); >+ PatchpointValue* patchpoint = m_out.patchpoint(Int64); >+ patchpoint->appendSomeRegister(value); >+ patchpoint->appendSomeRegister(prototype); >+ patchpoint->append(m_tagMask, ValueRep::lateReg(GPRInfo::tagMaskRegister)); >+ patchpoint->append(m_tagTypeNumber, ValueRep::lateReg(GPRInfo::tagTypeNumberRegister)); >+ patchpoint->numGPScratchRegisters = 2; >+ patchpoint->resultConstraint = ValueRep::SomeEarlyRegister; >+ patchpoint->clobber(RegisterSet::macroScratchRegisters()); > >- m_out.appendTo(loop, loadPrototypeBits); >- LValue value = m_out.phi(Int64, originalValue); >- LValue type = m_out.load8ZeroExt32(value, m_heaps.JSCell_typeInfoType); >- m_out.branch( >- m_out.notEqual(type, m_out.constInt32(ProxyObjectType)), >- usually(loadPrototypeBits), rarely(defaultHasInstanceSlow)); >- >- m_out.appendTo(loadPrototypeBits, loadPolyProto); >- LValue structure = loadStructure(value); >- >- LValue prototypeBits = m_out.load64(structure, m_heaps.Structure_prototype); >- ValueFromBlock directPrototype = m_out.anchor(prototypeBits); >- m_out.branch(m_out.isZero64(prototypeBits), unsure(loadPolyProto), unsure(comparePrototype)); >- >- m_out.appendTo(loadPolyProto, comparePrototype); >- ValueFromBlock polyProto = m_out.anchor( >- m_out.load64(m_out.baseIndex(m_heaps.properties.atAnyNumber(), value, m_out.constInt64(knownPolyProtoOffset), ScaleEight, JSObject::offsetOfInlineStorage()))); >- m_out.jump(comparePrototype); >- >- m_out.appendTo(comparePrototype, notYetInstance); >- LValue currentPrototype = m_out.phi(Int64, directPrototype, polyProto); >- ValueFromBlock isInstanceResult = m_out.anchor(m_out.booleanTrue); >- m_out.branch( >- m_out.equal(currentPrototype, prototype), >- unsure(continuation), unsure(notYetInstance)); >- >- m_out.appendTo(notYetInstance, defaultHasInstanceSlow); >- ValueFromBlock notInstanceResult = m_out.anchor(m_out.booleanFalse); >- m_out.addIncomingToPhi(value, m_out.anchor(currentPrototype)); >- m_out.branch(isCell(currentPrototype), unsure(loop), unsure(continuation)); >- >- m_out.appendTo(defaultHasInstanceSlow, continuation); >- // We can use the value that we're looping with because we >- // can just continue off from wherever we bailed from the >- // loop. >- ValueFromBlock defaultHasInstanceResult = m_out.anchor( >- vmCall(Int32, m_out.operation(operationDefaultHasInstance), m_callFrame, value, prototype)); >- m_out.jump(continuation); >+ RefPtr<PatchpointExceptionHandle> exceptionHandle = >+ preparePatchpointForExceptions(patchpoint); >+ >+ patchpoint->setGenerator( >+ [=] (CCallHelpers& jit, const StackmapGenerationParams& params) { >+ AllowMacroScratchRegisterUsage allowScratch(jit); >+ >+ GPRReg resultGPR = params[0].gpr(); >+ GPRReg valueGPR = params[1].gpr(); >+ GPRReg prototypeGPR = params[2].gpr(); >+ GPRReg scratchGPR = params.gpScratch(0); >+ GPRReg scratch2GPR = params.gpScratch(1); >+ >+ CCallHelpers::Jump doneJump; >+ if (!valueIsCell) { >+ CCallHelpers::Jump isCell = jit.branchIfCell(valueGPR); >+ jit.boxBooleanPayload(false, resultGPR); >+ doneJump = jit.jump(); >+ isCell.link(&jit); >+ } >+ >+ CCallHelpers::JumpList slowCases; >+ if (!prototypeIsCell) >+ slowCases.append(jit.branchIfNotCell(prototypeGPR)); >+ >+ CallSiteIndex callSiteIndex = >+ state->jitCode->common.addUniqueCallSiteIndex(node->origin.semantic); >+ >+ // This is the direct exit target for operation calls. >+ Box<CCallHelpers::JumpList> exceptions = >+ exceptionHandle->scheduleExitCreation(params)->jumps(jit); >+ >+ auto generator = Box<JITInstanceOfGenerator>::create( >+ jit.codeBlock(), node->origin.semantic, callSiteIndex, >+ params.unavailableRegisters(), resultGPR, valueGPR, prototypeGPR, scratchGPR, >+ scratch2GPR, prototypeIsObject); >+ generator->generateFastPath(jit); >+ CCallHelpers::Label done = jit.label(); >+ >+ params.addLatePath( >+ [=] (CCallHelpers& jit) { >+ AllowMacroScratchRegisterUsage allowScratch(jit); >+ >+ J_JITOperation_ESsiJJ optimizationFunction = operationInstanceOfOptimize; >+ >+ slowCases.link(&jit); >+ CCallHelpers::Label slowPathBegin = jit.label(); >+ CCallHelpers::Call slowPathCall = callOperation( >+ *state, params.unavailableRegisters(), jit, node->origin.semantic, >+ exceptions.get(), optimizationFunction, resultGPR, >+ CCallHelpers::TrustedImmPtr(generator->stubInfo()), valueGPR, >+ prototypeGPR).call(); >+ jit.jump().linkTo(done, &jit); >+ >+ generator->reportSlowPathCall(slowPathBegin, slowPathCall); >+ >+ jit.addLinkTask( >+ [=] (LinkBuffer& linkBuffer) { >+ generator->finalize(linkBuffer, linkBuffer); >+ }); >+ }); >+ >+ if (doneJump.isSet()) >+ doneJump.link(&jit); >+ }); > >- m_out.appendTo(continuation, lastNext); >- setBoolean( >- m_out.phi(Int32, notCellResult, isInstanceResult, notInstanceResult, defaultHasInstanceResult)); >+ // This returns a boxed boolean. >+ setJSValue(patchpoint); > } > > void compileInstanceOfCustom() >@@ -11454,7 +11482,7 @@ private: > Node* node = m_node; > UniquedStringImpl* uid = m_graph.identifiers()[node->identifierNumber()]; > >- B3::PatchpointValue* patchpoint = m_out.patchpoint(Int64); >+ PatchpointValue* patchpoint = m_out.patchpoint(Int64); > patchpoint->appendSomeRegister(base); > patchpoint->append(m_tagMask, ValueRep::lateReg(GPRInfo::tagMaskRegister)); > patchpoint->append(m_tagTypeNumber, ValueRep::lateReg(GPRInfo::tagTypeNumberRegister)); >@@ -11513,7 +11541,7 @@ private: > > jit.addLinkTask( > [=] (LinkBuffer& linkBuffer) { >- generator->finalize(linkBuffer); >+ generator->finalize(linkBuffer, linkBuffer); > }); > }); > }); >@@ -11526,7 +11554,7 @@ private: > Node* node = m_node; > UniquedStringImpl* uid = m_graph.identifiers()[node->identifierNumber()]; > >- B3::PatchpointValue* patchpoint = m_out.patchpoint(Int64); >+ PatchpointValue* patchpoint = m_out.patchpoint(Int64); > patchpoint->appendSomeRegister(base); > patchpoint->appendSomeRegister(thisValue); > patchpoint->append(m_tagMask, ValueRep::lateReg(GPRInfo::tagMaskRegister)); >@@ -11581,14 +11609,14 @@ private: > > jit.addLinkTask( > [=] (LinkBuffer& linkBuffer) { >- generator->finalize(linkBuffer); >+ generator->finalize(linkBuffer, linkBuffer); > }); > }); > }); > > return patchpoint; > } >- >+ > LValue isFastTypedArray(LValue object) > { > return m_out.equal( >Index: Source/JavaScriptCore/jit/ICStats.h >=================================================================== >--- Source/JavaScriptCore/jit/ICStats.h (revision 231917) >+++ Source/JavaScriptCore/jit/ICStats.h (working copy) >@@ -43,6 +43,8 @@ namespace JSC { > macro(GetByIdSelfPatch) \ > macro(InAddAccessCase) \ > macro(InReplaceWithJump) \ >+ macro(InstanceOfAddAccessCase) \ >+ macro(InstanceOfReplaceWithJump) \ > macro(OperationGetById) \ > macro(OperationGetByIdGeneric) \ > macro(OperationGetByIdBuildList) \ >Index: Source/JavaScriptCore/jit/JIT.cpp >=================================================================== >--- Source/JavaScriptCore/jit/JIT.cpp (revision 231917) >+++ Source/JavaScriptCore/jit/JIT.cpp (working copy) >@@ -478,6 +478,7 @@ void JIT::privateCompileSlowCases() > m_getByIdIndex = 0; > m_getByIdWithThisIndex = 0; > m_putByIdIndex = 0; >+ m_instanceOfIndex = 0; > m_byValInstructionIndex = 0; > m_callLinkInfoIndex = 0; > >@@ -599,6 +600,7 @@ void JIT::privateCompileSlowCases() > RELEASE_ASSERT(m_getByIdIndex == m_getByIds.size()); > RELEASE_ASSERT(m_getByIdWithThisIndex == m_getByIdsWithThis.size()); > RELEASE_ASSERT(m_putByIdIndex == m_putByIds.size()); >+ RELEASE_ASSERT(m_instanceOfIndex == m_instanceOfs.size()); > RELEASE_ASSERT(m_callLinkInfoIndex == m_callCompilationInfo.size()); > RELEASE_ASSERT(numberOfValueProfiles == m_codeBlock->numberOfValueProfiles()); > >@@ -836,13 +838,11 @@ CompilationResult JIT::link() > if (record.callee) > patchBuffer.link(record.from, record.callee); > } >- >- for (unsigned i = m_getByIds.size(); i--;) >- m_getByIds[i].finalize(patchBuffer); >- for (unsigned i = m_getByIdsWithThis.size(); i--;) >- m_getByIdsWithThis[i].finalize(patchBuffer); >- for (unsigned i = m_putByIds.size(); i--;) >- m_putByIds[i].finalize(patchBuffer); >+ >+ finalizeInlineCaches(m_getByIds, patchBuffer); >+ finalizeInlineCaches(m_getByIdsWithThis, patchBuffer); >+ finalizeInlineCaches(m_putByIds, patchBuffer); >+ finalizeInlineCaches(m_instanceOfs, patchBuffer); > > if (m_byValCompilationInfo.size()) { > CodeLocationLabel<ExceptionHandlerPtrTag> exceptionHandler = patchBuffer.locationOf<ExceptionHandlerPtrTag>(m_exceptionHandler); >Index: Source/JavaScriptCore/jit/JIT.h >=================================================================== >--- Source/JavaScriptCore/jit/JIT.h (revision 231917) >+++ Source/JavaScriptCore/jit/JIT.h (working copy) >@@ -865,6 +865,7 @@ namespace JSC { > Vector<JITGetByIdGenerator> m_getByIds; > Vector<JITGetByIdWithThisGenerator> m_getByIdsWithThis; > Vector<JITPutByIdGenerator> m_putByIds; >+ Vector<JITInstanceOfGenerator> m_instanceOfs; > Vector<ByValCompilationInfo> m_byValCompilationInfo; > Vector<CallCompilationInfo> m_callCompilationInfo; > Vector<JumpTable> m_jmpTable; >@@ -880,6 +881,7 @@ namespace JSC { > unsigned m_getByIdIndex; > unsigned m_getByIdWithThisIndex; > unsigned m_putByIdIndex; >+ unsigned m_instanceOfIndex; > unsigned m_byValInstructionIndex; > unsigned m_callLinkInfoIndex; > >Index: Source/JavaScriptCore/jit/JITInlineCacheGenerator.cpp >=================================================================== >--- Source/JavaScriptCore/jit/JITInlineCacheGenerator.cpp (revision 231917) >+++ Source/JavaScriptCore/jit/JITInlineCacheGenerator.cpp (working copy) >@@ -43,23 +43,39 @@ static StructureStubInfo* garbageStubInf > } > > JITInlineCacheGenerator::JITInlineCacheGenerator( >- CodeBlock* codeBlock, CodeOrigin codeOrigin, CallSiteIndex callSite, AccessType accessType) >+ CodeBlock* codeBlock, CodeOrigin codeOrigin, CallSiteIndex callSite, AccessType accessType, >+ const RegisterSet& usedRegisters) > : m_codeBlock(codeBlock) > { > m_stubInfo = m_codeBlock ? m_codeBlock->addStubInfo(accessType) : garbageStubInfo(); > m_stubInfo->codeOrigin = codeOrigin; > m_stubInfo->callSiteIndex = callSite; >+ >+ m_stubInfo->patch.usedRegisters = usedRegisters; >+} >+ >+void JITInlineCacheGenerator::finalize( >+ LinkBuffer& fastPath, LinkBuffer& slowPath, CodeLocationLabel<JITStubRoutinePtrTag> start) >+{ >+ m_stubInfo->patch.start = start; >+ >+ int32_t inlineSize = MacroAssembler::differenceBetweenCodePtr( >+ start, fastPath.locationOf<NoPtrTag>(m_done)); >+ m_stubInfo->patch.inlineSize = inlineSize; >+ >+ m_stubInfo->patch.deltaFromStartToSlowPathCallLocation = MacroAssembler::differenceBetweenCodePtr( >+ start, slowPath.locationOf<NoPtrTag>(m_slowPathCall)); >+ m_stubInfo->patch.deltaFromStartToSlowPathStart = MacroAssembler::differenceBetweenCodePtr( >+ start, slowPath.locationOf<NoPtrTag>(m_slowPathBegin)); > } > > JITByIdGenerator::JITByIdGenerator( > CodeBlock* codeBlock, CodeOrigin codeOrigin, CallSiteIndex callSite, AccessType accessType, > const RegisterSet& usedRegisters, JSValueRegs base, JSValueRegs value) >- : JITInlineCacheGenerator(codeBlock, codeOrigin, callSite, accessType) >+ : JITInlineCacheGenerator(codeBlock, codeOrigin, callSite, accessType, usedRegisters) > , m_base(base) > , m_value(value) > { >- m_stubInfo->patch.usedRegisters = usedRegisters; >- > m_stubInfo->patch.baseGPR = static_cast<int8_t>(base.payloadGPR()); > m_stubInfo->patch.valueGPR = static_cast<int8_t>(value.payloadGPR()); > m_stubInfo->patch.thisGPR = static_cast<int8_t>(InvalidGPRReg); >@@ -73,23 +89,8 @@ JITByIdGenerator::JITByIdGenerator( > void JITByIdGenerator::finalize(LinkBuffer& fastPath, LinkBuffer& slowPath) > { > ASSERT(m_start.isSet()); >- CodeLocationLabel<JITStubRoutinePtrTag> start = fastPath.locationOf<JITStubRoutinePtrTag>(m_start); >- m_stubInfo->patch.start = start; >- >- int32_t inlineSize = MacroAssembler::differenceBetweenCodePtr( >- start, fastPath.locationOf<NoPtrTag>(m_done)); >- ASSERT(inlineSize > 0); >- m_stubInfo->patch.inlineSize = inlineSize; >- >- m_stubInfo->patch.deltaFromStartToSlowPathCallLocation = MacroAssembler::differenceBetweenCodePtr( >- start, slowPath.locationOf<NoPtrTag>(m_slowPathCall)); >- m_stubInfo->patch.deltaFromStartToSlowPathStart = MacroAssembler::differenceBetweenCodePtr( >- start, slowPath.locationOf<NoPtrTag>(m_slowPathBegin)); >-} >- >-void JITByIdGenerator::finalize(LinkBuffer& linkBuffer) >-{ >- finalize(linkBuffer, linkBuffer); >+ JITInlineCacheGenerator::finalize( >+ fastPath, slowPath, fastPath.locationOf<JITStubRoutinePtrTag>(m_start)); > } > > void JITByIdGenerator::generateFastCommon(MacroAssembler& jit, size_t inlineICSize) >@@ -165,6 +166,46 @@ V_JITOperation_ESsiJJI JITPutByIdGenerat > return operationPutByIdNonStrictOptimize; > } > >+JITInstanceOfGenerator::JITInstanceOfGenerator( >+ CodeBlock* codeBlock, CodeOrigin codeOrigin, CallSiteIndex callSiteIndex, >+ const RegisterSet& usedRegisters, GPRReg result, GPRReg value, GPRReg prototype, >+ GPRReg scratch1, GPRReg scratch2, bool prototypeIsKnownObject) >+ : JITInlineCacheGenerator( >+ codeBlock, codeOrigin, callSiteIndex, AccessType::InstanceOf, usedRegisters) >+{ >+ m_stubInfo->patch.baseGPR = static_cast<int8_t>(value); >+ m_stubInfo->patch.valueGPR = static_cast<int8_t>(result); >+ m_stubInfo->patch.thisGPR = static_cast<int8_t>(prototype); >+#if USE(JSVALUE32_64) >+ m_stubInfo->patch.baseTagGPR = static_cast<int8_t>(InvalidGPRReg); >+ m_stubInfo->patch.valueTagGPR = static_cast<int8_t>(InvalidGPRReg); >+ m_stubInfo->patch.thisTagGPR = static_cast<int8_t>(InvalidGPRReg); >+#endif >+ >+ m_stubInfo->patch.usedRegisters.clear(result); >+ if (scratch1 != InvalidGPRReg) >+ m_stubInfo->patch.usedRegisters.clear(scratch1); >+ if (scratch2 != InvalidGPRReg) >+ m_stubInfo->patch.usedRegisters.clear(scratch2); >+ >+ m_stubInfo->prototypeIsKnownObject = prototypeIsKnownObject; >+} >+ >+void JITInstanceOfGenerator::generateFastPath(MacroAssembler& jit) >+{ >+ m_jump = jit.patchableJump(); >+ m_done = jit.label(); >+} >+ >+void JITInstanceOfGenerator::finalize(LinkBuffer& fastPath, LinkBuffer& slowPath) >+{ >+ JITInlineCacheGenerator::finalize( >+ fastPath, slowPath, >+ fastPath.locationOf<JITStubRoutinePtrTag>(m_jump)); >+ >+ fastPath.link(m_jump.m_jump, slowPath.locationOf<NoPtrTag>(m_slowPathBegin)); >+} >+ > } // namespace JSC > > #endif // ENABLE(JIT) >Index: Source/JavaScriptCore/jit/JITInlineCacheGenerator.h >=================================================================== >--- Source/JavaScriptCore/jit/JITInlineCacheGenerator.h (revision 231917) >+++ Source/JavaScriptCore/jit/JITInlineCacheGenerator.h (working copy) >@@ -1,5 +1,5 @@ > /* >- * Copyright (C) 2013, 2015 Apple Inc. All rights reserved. >+ * Copyright (C) 2013-2018 Apple Inc. All rights reserved. > * > * Redistribution and use in source and binary forms, with or without > * modification, are permitted provided that the following conditions >@@ -45,14 +45,31 @@ enum class AccessType : int8_t; > class JITInlineCacheGenerator { > protected: > JITInlineCacheGenerator() { } >- JITInlineCacheGenerator(CodeBlock*, CodeOrigin, CallSiteIndex, AccessType); >+ JITInlineCacheGenerator( >+ CodeBlock*, CodeOrigin, CallSiteIndex, AccessType, const RegisterSet& usedRegisters); > > public: > StructureStubInfo* stubInfo() const { return m_stubInfo; } > >+ void reportSlowPathCall(MacroAssembler::Label slowPathBegin, MacroAssembler::Call call) >+ { >+ m_slowPathBegin = slowPathBegin; >+ m_slowPathCall = call; >+ } >+ >+ MacroAssembler::Label slowPathBegin() const { return m_slowPathBegin; } >+ >+ void finalize( >+ LinkBuffer& fastPathLinkBuffer, LinkBuffer& slowPathLinkBuffer, >+ CodeLocationLabel<JITStubRoutinePtrTag> start); >+ > protected: > CodeBlock* m_codeBlock; > StructureStubInfo* m_stubInfo; >+ >+ MacroAssembler::Label m_done; >+ MacroAssembler::Label m_slowPathBegin; >+ MacroAssembler::Call m_slowPathCall; > }; > > class JITByIdGenerator : public JITInlineCacheGenerator { >@@ -60,36 +77,27 @@ protected: > JITByIdGenerator() { } > > JITByIdGenerator( >- CodeBlock*, CodeOrigin, CallSiteIndex, AccessType, const RegisterSet&, JSValueRegs base, >- JSValueRegs value); >- >+ CodeBlock*, CodeOrigin, CallSiteIndex, AccessType, const RegisterSet& usedRegisters, >+ JSValueRegs base, JSValueRegs value); >+ > public: >- void reportSlowPathCall(MacroAssembler::Label slowPathBegin, MacroAssembler::Call call) >- { >- m_slowPathBegin = slowPathBegin; >- m_slowPathCall = call; >- } >- >- MacroAssembler::Label slowPathBegin() const { return m_slowPathBegin; } > MacroAssembler::Jump slowPathJump() const > { > ASSERT(m_slowPathJump.isSet()); > return m_slowPathJump; > } > >- void finalize(LinkBuffer& fastPathLinkBuffer, LinkBuffer& slowPathLinkBuffer); >- void finalize(LinkBuffer&); >+ void finalize( >+ LinkBuffer& fastPathLinkBuffer, LinkBuffer& slowPathLinkBuffer); > > protected: >+ > void generateFastCommon(MacroAssembler&, size_t size); > > JSValueRegs m_base; > JSValueRegs m_value; >- >+ > MacroAssembler::Label m_start; >- MacroAssembler::Label m_done; >- MacroAssembler::Label m_slowPathBegin; >- MacroAssembler::Call m_slowPathCall; > MacroAssembler::Jump m_slowPathJump; > }; > >@@ -124,7 +132,7 @@ public: > > JITPutByIdGenerator( > CodeBlock*, CodeOrigin, CallSiteIndex, const RegisterSet& usedRegisters, JSValueRegs base, >- JSValueRegs, GPRReg scratch, ECMAMode, PutKind); >+ JSValueRegs value, GPRReg scratch, ECMAMode, PutKind); > > void generateFastPath(MacroAssembler&); > >@@ -135,6 +143,36 @@ private: > PutKind m_putKind; > }; > >+class JITInstanceOfGenerator : public JITInlineCacheGenerator { >+public: >+ JITInstanceOfGenerator() { } >+ >+ JITInstanceOfGenerator( >+ CodeBlock*, CodeOrigin, CallSiteIndex, const RegisterSet& usedRegisters, GPRReg result, >+ GPRReg value, GPRReg prototype, GPRReg scratch1, GPRReg scratch2, >+ bool prototypeIsKnownObject = false); >+ >+ void generateFastPath(MacroAssembler&); >+ >+ void finalize(LinkBuffer& fastPathLinkBuffer, LinkBuffer& slowPathLinkBuffer); >+ >+private: >+ MacroAssembler::PatchableJump m_jump; >+}; >+ >+template<typename VectorType> >+void finalizeInlineCaches(VectorType& vector, LinkBuffer& fastPath, LinkBuffer& slowPath) >+{ >+ for (auto& entry : vector) >+ entry.finalize(fastPath, slowPath); >+} >+ >+template<typename VectorType> >+void finalizeInlineCaches(VectorType& vector, LinkBuffer& linkBuffer) >+{ >+ finalizeInlineCaches(vector, linkBuffer, linkBuffer); >+} >+ > } // namespace JSC > > #endif // ENABLE(JIT) >Index: Source/JavaScriptCore/jit/JITOpcodes32_64.cpp >=================================================================== >--- Source/JavaScriptCore/jit/JITOpcodes32_64.cpp (revision 231917) >+++ Source/JavaScriptCore/jit/JITOpcodes32_64.cpp (working copy) >@@ -151,34 +151,16 @@ void JIT::emit_op_instanceof(Instruction > emitJumpSlowCaseIfNotJSCell(value); > emitJumpSlowCaseIfNotJSCell(proto); > >- // Check that prototype is an object >- addSlowCase(branchIfNotObject(regT1)); >- >- // Optimistically load the result true, and start looping. >- // Initially, regT1 still contains proto and regT2 still contains value. >- // As we loop regT2 will be updated with its prototype, recursively walking the prototype chain. >- move(TrustedImm32(1), regT0); >- Label loop(this); >- >- addSlowCase(branchIfType(regT2, ProxyObjectType)); >- >- // Load the prototype of the cell in regT2. If this is equal to regT1 - WIN! >- // Otherwise, check if we've hit null - if we have then drop out of the loop, if not go again. >- loadPtr(Address(regT2, JSCell::structureIDOffset()), regT4); >- load32(Address(regT4, Structure::prototypeOffset() + TagOffset), regT3); >- load32(Address(regT4, Structure::prototypeOffset() + PayloadOffset), regT4); >- auto hasMonoProto = branchIfNotEmpty(regT3); >- load32(Address(regT2, offsetRelativeToBase(knownPolyProtoOffset) + PayloadOffset), regT4); >- hasMonoProto.link(this); >- move(regT4, regT2); >- Jump isInstance = branchPtr(Equal, regT2, regT1); >- branchTest32(NonZero, regT2).linkTo(loop, this); >- >- // We get here either by dropping out of the loop, or if value was not an Object. Result is false. >- move(TrustedImm32(0), regT0); >- >- // isInstance jumps right down to here, to skip setting the result to false (it has already set true). >- isInstance.link(this); >+ JITInstanceOfGenerator gen( >+ m_codeBlock, CodeOrigin(m_bytecodeOffset), CallSiteIndex(m_bytecodeOffset), >+ RegisterSet::stubUnavailableRegisters(), >+ regT0, // result >+ regT2, // value >+ regT1, // proto >+ regT3, regT4); // scratch >+ gen.generateFastPath(*this); >+ m_instanceOfs.append(gen); >+ > emitStoreBool(dst, regT0); > } > >@@ -191,15 +173,19 @@ void JIT::emit_op_instanceof_custom(Inst > void JIT::emitSlow_op_instanceof(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter) > { > linkAllSlowCases(iter); >- >+ > auto& bytecode = *reinterpret_cast<OpInstanceof*>(currentInstruction); > int dst = bytecode.dst(); > int value = bytecode.value(); > int proto = bytecode.prototype(); >- >- emitLoad(value, regT1, regT0); >- emitLoad(proto, regT3, regT2); >- callOperation(operationInstanceOf, dst, JSValueRegs(regT1, regT0), JSValueRegs(regT3, regT2)); >+ >+ JITInstanceOfGenerator& gen = m_instanceOfs[m_instanceOfIndex++]; >+ >+ Label coldPathBegin = label(); >+ emitLoadTag(value, regT0); >+ emitLoadTag(proto, regT3); >+ Call call = callOperation(operationInstanceOfOptimize, dst, gen.stubInfo(), JSValueRegs(regT0, regT2), JSValueRegs(regT3, regT1)); >+ gen.reportSlowPathCall(coldPathBegin, call); > } > > void JIT::emitSlow_op_instanceof_custom(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter) >Index: Source/JavaScriptCore/jit/JITOpcodes.cpp >=================================================================== >--- Source/JavaScriptCore/jit/JITOpcodes.cpp (revision 231917) >+++ Source/JavaScriptCore/jit/JITOpcodes.cpp (working copy) >@@ -146,41 +146,37 @@ void JIT::emit_op_instanceof(Instruction > // We use regT0 for baseVal since we will be done with this first, and we can then use it for the result. > emitGetVirtualRegister(value, regT2); > emitGetVirtualRegister(proto, regT1); >- >+ > // Check that proto are cells. baseVal must be a cell - this is checked by the get_by_id for Symbol.hasInstance. > emitJumpSlowCaseIfNotJSCell(regT2, value); > emitJumpSlowCaseIfNotJSCell(regT1, proto); > >- // Check that prototype is an object >- addSlowCase(branchIfNotObject(regT1)); >+ JITInstanceOfGenerator gen( >+ m_codeBlock, CodeOrigin(m_bytecodeOffset), CallSiteIndex(m_bytecodeOffset), >+ RegisterSet::stubUnavailableRegisters(), >+ regT0, // result >+ regT2, // value >+ regT1, // proto >+ regT3, regT4); // scratch >+ gen.generateFastPath(*this); >+ m_instanceOfs.append(gen); > >- // Optimistically load the result true, and start looping. >- // Initially, regT1 still contains proto and regT2 still contains value. >- // As we loop regT2 will be updated with its prototype, recursively walking the prototype chain. >- move(TrustedImm64(JSValue::encode(jsBoolean(true))), regT0); >- Label loop(this); >- >- addSlowCase(branchIfType(regT2, ProxyObjectType)); >- >- // Load the prototype of the object in regT2. If this is equal to regT1 - WIN! >- // Otherwise, check if we've hit null - if we have then drop out of the loop, if not go again. >- emitLoadStructure(*vm(), regT2, regT4, regT3); >- load64(Address(regT4, Structure::prototypeOffset()), regT4); >- auto hasMonoProto = branchIfNotEmpty(regT4); >- load64(Address(regT2, offsetRelativeToBase(knownPolyProtoOffset)), regT4); >- hasMonoProto.link(this); >- move(regT4, regT2); >- Jump isInstance = branchPtr(Equal, regT2, regT1); >- branchIfCell(regT2).linkTo(loop, this); >- >- // We get here either by dropping out of the loop, or if value was not an Object. Result is false. >- move(TrustedImm64(JSValue::encode(jsBoolean(false))), regT0); >- >- // isInstance jumps right down to here, to skip setting the result to false (it has already set true). >- isInstance.link(this); > emitPutVirtualRegister(dst); > } > >+void JIT::emitSlow_op_instanceof(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter) >+{ >+ linkAllSlowCases(iter); >+ >+ int resultVReg = currentInstruction[1].u.operand; >+ >+ JITInstanceOfGenerator& gen = m_instanceOfs[m_instanceOfIndex++]; >+ >+ Label coldPathBegin = label(); >+ Call call = callOperation(operationInstanceOfOptimize, resultVReg, gen.stubInfo(), regT2, regT1); >+ gen.reportSlowPathCall(coldPathBegin, call); >+} >+ > void JIT::emit_op_instanceof_custom(Instruction*) > { > // This always goes to slow path since we expect it to be rare. >@@ -905,20 +901,6 @@ void JIT::emitSlow_op_jneq(Instruction* > emitJumpSlowToHot(branchTest32(Zero, returnValueGPR), target); > } > >-void JIT::emitSlow_op_instanceof(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter) >-{ >- linkAllSlowCases(iter); >- >- auto& bytecode = *reinterpret_cast<OpInstanceof*>(currentInstruction); >- int dst = bytecode.dst(); >- int value = bytecode.value(); >- int proto = bytecode.prototype(); >- >- emitGetVirtualRegister(value, regT0); >- emitGetVirtualRegister(proto, regT1); >- callOperation(operationInstanceOf, dst, regT0, regT1); >-} >- > void JIT::emitSlow_op_instanceof_custom(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter) > { > linkAllSlowCases(iter); >Index: Source/JavaScriptCore/jit/JITOperations.cpp >=================================================================== >--- Source/JavaScriptCore/jit/JITOperations.cpp (revision 231917) >+++ Source/JavaScriptCore/jit/JITOperations.cpp (working copy) >@@ -2181,6 +2181,34 @@ EncodedJSValue JIT_OPERATION operationIn > return JSValue::encode(jsBoolean(result)); > } > >+EncodedJSValue JIT_OPERATION operationInstanceOfGeneric(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue encodedValue, EncodedJSValue encodedProto) >+{ >+ VM& vm = exec->vm(); >+ NativeCallFrameTracer tracer(&vm, exec); >+ JSValue value = JSValue::decode(encodedValue); >+ JSValue proto = JSValue::decode(encodedProto); >+ >+ stubInfo->tookSlowPath = true; >+ >+ bool result = JSObject::defaultHasInstance(exec, value, proto); >+ return JSValue::encode(jsBoolean(result)); >+} >+ >+EncodedJSValue JIT_OPERATION operationInstanceOfOptimize(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue encodedValue, EncodedJSValue encodedProto) >+{ >+ VM& vm = exec->vm(); >+ NativeCallFrameTracer tracer(&vm, exec); >+ JSValue value = JSValue::decode(encodedValue); >+ JSValue proto = JSValue::decode(encodedProto); >+ >+ bool result = JSObject::defaultHasInstance(exec, value, proto); >+ >+ if (stubInfo->considerCaching(exec->codeBlock(), value.structureOrNull())) >+ repatchInstanceOf(exec, value, proto, *stubInfo, result); >+ >+ return JSValue::encode(jsBoolean(result)); >+} >+ > int32_t JIT_OPERATION operationSizeFrameForForwardArguments(ExecState* exec, EncodedJSValue, int32_t numUsedStackSlots, int32_t) > { > VM& vm = exec->vm(); >Index: Source/JavaScriptCore/jit/JITOperations.h >=================================================================== >--- Source/JavaScriptCore/jit/JITOperations.h (revision 231917) >+++ Source/JavaScriptCore/jit/JITOperations.h (working copy) >@@ -174,6 +174,7 @@ typedef EncodedJSValue (JIT_OPERATION *J > typedef EncodedJSValue (JIT_OPERATION *J_JITOperation_ESS)(ExecState*, size_t, size_t); > typedef EncodedJSValue (JIT_OPERATION *J_JITOperation_ESsiCI)(ExecState*, StructureStubInfo*, JSCell*, UniquedStringImpl*); > typedef EncodedJSValue (JIT_OPERATION *J_JITOperation_ESsiJI)(ExecState*, StructureStubInfo*, EncodedJSValue, UniquedStringImpl*); >+typedef EncodedJSValue (JIT_OPERATION *J_JITOperation_ESsiJJ)(ExecState*, StructureStubInfo*, EncodedJSValue, EncodedJSValue); > typedef EncodedJSValue (JIT_OPERATION *J_JITOperation_ESsiJJI)(ExecState*, StructureStubInfo*, EncodedJSValue, EncodedJSValue, UniquedStringImpl*); > typedef EncodedJSValue (JIT_OPERATION *J_JITOperation_EZ)(ExecState*, int32_t); > typedef EncodedJSValue (JIT_OPERATION *J_JITOperation_EZIcfZ)(ExecState*, int32_t, InlineCallFrame*, int32_t); >@@ -451,7 +452,9 @@ size_t JIT_OPERATION operationDeleteByVa > JSCell* JIT_OPERATION operationPushWithScope(ExecState*, JSCell* currentScopeCell, EncodedJSValue object) WTF_INTERNAL; > JSCell* JIT_OPERATION operationPushWithScopeObject(ExecState* exec, JSCell* currentScopeCell, JSObject* object) WTF_INTERNAL; > JSCell* JIT_OPERATION operationGetPNames(ExecState*, JSObject*) WTF_INTERNAL; >-EncodedJSValue JIT_OPERATION operationInstanceOf(ExecState*, EncodedJSValue, EncodedJSValue proto) WTF_INTERNAL; >+EncodedJSValue JIT_OPERATION operationInstanceOf(ExecState*, EncodedJSValue value, EncodedJSValue proto) WTF_INTERNAL; >+EncodedJSValue JIT_OPERATION operationInstanceOfGeneric(ExecState*, StructureStubInfo*, EncodedJSValue value, EncodedJSValue proto) WTF_INTERNAL; >+EncodedJSValue JIT_OPERATION operationInstanceOfOptimize(ExecState*, StructureStubInfo*, EncodedJSValue value, EncodedJSValue proto) WTF_INTERNAL; > int32_t JIT_OPERATION operationSizeFrameForForwardArguments(ExecState*, EncodedJSValue arguments, int32_t numUsedStackSlots, int32_t firstVarArgOffset) WTF_INTERNAL; > int32_t JIT_OPERATION operationSizeFrameForVarargs(ExecState*, EncodedJSValue arguments, int32_t numUsedStackSlots, int32_t firstVarArgOffset) WTF_INTERNAL; > CallFrame* JIT_OPERATION operationSetupForwardArgumentsFrame(ExecState*, CallFrame*, EncodedJSValue, int32_t, int32_t length) WTF_INTERNAL; >Index: Source/JavaScriptCore/jit/JITPropertyAccess.cpp >=================================================================== >--- Source/JavaScriptCore/jit/JITPropertyAccess.cpp (revision 231917) >+++ Source/JavaScriptCore/jit/JITPropertyAccess.cpp (working copy) >@@ -1314,7 +1314,7 @@ void JIT::privateCompileGetByValWithCach > if (callSite.callee) > patchBuffer.link(callSite.from, callSite.callee); > } >- gen.finalize(patchBuffer); >+ gen.finalize(patchBuffer, patchBuffer); > > byValInfo->stubRoutine = FINALIZE_CODE_FOR_STUB( > m_codeBlock, patchBuffer, JITStubRoutinePtrTag, >@@ -1404,7 +1404,7 @@ void JIT::privateCompilePutByValWithCach > if (callSite.callee) > patchBuffer.link(callSite.from, callSite.callee); > } >- gen.finalize(patchBuffer); >+ gen.finalize(patchBuffer, patchBuffer); > > byValInfo->stubRoutine = FINALIZE_CODE_FOR_STUB( > m_codeBlock, patchBuffer, JITStubRoutinePtrTag, >Index: Source/JavaScriptCore/jit/RegisterSet.cpp >=================================================================== >--- Source/JavaScriptCore/jit/RegisterSet.cpp (revision 231917) >+++ Source/JavaScriptCore/jit/RegisterSet.cpp (working copy) >@@ -84,6 +84,9 @@ RegisterSet RegisterSet::volatileRegiste > > RegisterSet RegisterSet::stubUnavailableRegisters() > { >+ // FIXME: This is overly conservative. We could subtract out those callee-saves that we >+ // actually saved. >+ // https://bugs.webkit.org/show_bug.cgi?id=185686 > return RegisterSet(specialRegisters(), vmCalleeSaveRegisters()); > } > >Index: Source/JavaScriptCore/jit/Repatch.cpp >=================================================================== >--- Source/JavaScriptCore/jit/Repatch.cpp (revision 231917) >+++ Source/JavaScriptCore/jit/Repatch.cpp (working copy) >@@ -43,6 +43,7 @@ > #include "GetterSetterAccessCase.h" > #include "ICStats.h" > #include "InlineAccess.h" >+#include "InstanceOfAccessCase.h" > #include "IntrinsicGetterAccessCase.h" > #include "JIT.h" > #include "JITInlines.h" >@@ -612,7 +613,7 @@ static InlineCacheAction tryCacheIn( > AccessGenerationResult result; > > { >- GCSafeConcurrentJSLocker locker(exec->codeBlock()->m_lock, exec->vm().heap); >+ GCSafeConcurrentJSLocker locker(exec->codeBlock()->m_lock, vm.heap); > if (forceICFailure(exec)) > return GiveUpOnCache; > >@@ -673,7 +674,7 @@ static InlineCacheAction tryCacheIn( > RELEASE_ASSERT(result.code()); > > MacroAssembler::repatchJump( >- stubInfo.patchableJumpForIn(), >+ stubInfo.patchableJump(), > CodeLocationLabel<JITStubRoutinePtrTag>(result.code())); > } > } >@@ -692,6 +693,80 @@ void repatchIn( > ftlThunkAwareRepatchCall(exec->codeBlock(), stubInfo.slowPathCallLocation(), operationIn); > } > >+static InlineCacheAction tryCacheInstanceOf( >+ ExecState* exec, JSValue valueValue, JSValue prototypeValue, StructureStubInfo& stubInfo, >+ bool wasFound) >+{ >+ VM& vm = exec->vm(); >+ CodeBlock* codeBlock = exec->codeBlock(); >+ AccessGenerationResult result; >+ >+ RELEASE_ASSERT(valueValue.isCell()); // shouldConsiderCaching rejects non-cells. >+ >+ if (forceICFailure(exec)) >+ return GiveUpOnCache; >+ >+ { >+ GCSafeConcurrentJSLocker locker(codeBlock->m_lock, vm.heap); >+ >+ JSCell* value = valueValue.asCell(); >+ JSObject* prototype = jsDynamicCast<JSObject*>(vm, prototypeValue); >+ >+ Structure* structure = value->structure(vm); >+ >+ std::unique_ptr<AccessCase> newCase; >+ >+ if (!jsDynamicCast<JSObject*>(vm, value)) { >+ newCase = InstanceOfAccessCase::create( >+ vm, codeBlock, AccessCase::InstanceOfMiss, structure, ObjectPropertyConditionSet(), >+ prototype); >+ } else if (prototype && structure->prototypeQueriesAreCacheable()) { >+ // FIXME: Teach this to do poly proto. >+ // https://bugs.webkit.org/show_bug.cgi?id=185663 >+ >+ ObjectPropertyConditionSet conditionSet = generateConditionsForInstanceOf( >+ vm, codeBlock, exec, structure, prototype, wasFound); >+ >+ if (conditionSet.isValid()) { >+ newCase = InstanceOfAccessCase::create( >+ vm, codeBlock, >+ wasFound ? AccessCase::InstanceOfHit : AccessCase::InstanceOfMiss, >+ structure, conditionSet, prototype); >+ } >+ } >+ >+ if (!newCase) >+ newCase = AccessCase::create(vm, codeBlock, AccessCase::InstanceOfGeneric); >+ >+ LOG_IC((ICEvent::InstanceOfAddAccessCase, structure->classInfo(), Identifier())); >+ >+ result = stubInfo.addAccessCase(locker, codeBlock, Identifier(), WTFMove(newCase)); >+ >+ if (result.generatedSomeCode()) { >+ LOG_IC((ICEvent::InstanceOfReplaceWithJump, structure->classInfo(), Identifier())); >+ >+ RELEASE_ASSERT(result.code()); >+ >+ MacroAssembler::repatchJump( >+ stubInfo.patchableJump(), >+ CodeLocationLabel<JITStubRoutinePtrTag>(result.code())); >+ } >+ } >+ >+ fireWatchpointsAndClearStubIfNeeded(vm, stubInfo, codeBlock, result); >+ >+ return result.shouldGiveUpNow() ? GiveUpOnCache : RetryCacheLater; >+} >+ >+void repatchInstanceOf( >+ ExecState* exec, JSValue valueValue, JSValue prototypeValue, StructureStubInfo& stubInfo, >+ bool wasFound) >+{ >+ SuperSamplerScope superSamplerScope(false); >+ if (tryCacheInstanceOf(exec, valueValue, prototypeValue, stubInfo, wasFound) == GiveUpOnCache) >+ ftlThunkAwareRepatchCall(exec->codeBlock(), stubInfo.slowPathCallLocation(), operationInstanceOfGeneric); >+} >+ > static void linkSlowFor(VM*, CallLinkInfo& callLinkInfo, MacroAssemblerCodeRef<JITStubRoutinePtrTag> codeRef) > { > MacroAssembler::repatchNearCall(callLinkInfo.callReturnLocation(), CodeLocationLabel<JITStubRoutinePtrTag>(codeRef.code())); >@@ -1156,9 +1231,19 @@ void resetPutByID(CodeBlock* codeBlock, > InlineAccess::rewireStubAsJump(stubInfo, stubInfo.slowPathStartLocation()); > } > >-void resetIn(CodeBlock*, StructureStubInfo& stubInfo) >+static void resetPatchableJump(StructureStubInfo& stubInfo) >+{ >+ MacroAssembler::repatchJump(stubInfo.patchableJump(), stubInfo.slowPathStartLocation()); >+} >+ >+void resetIn(StructureStubInfo& stubInfo) >+{ >+ resetPatchableJump(stubInfo); >+} >+ >+void resetInstanceOf(StructureStubInfo& stubInfo) > { >- MacroAssembler::repatchJump(stubInfo.patchableJumpForIn(), stubInfo.slowPathStartLocation()); >+ resetPatchableJump(stubInfo); > } > > } // namespace JSC >Index: Source/JavaScriptCore/jit/Repatch.h >=================================================================== >--- Source/JavaScriptCore/jit/Repatch.h (revision 231917) >+++ Source/JavaScriptCore/jit/Repatch.h (working copy) >@@ -45,6 +45,7 @@ void buildGetByIDProtoList(ExecState*, J > void repatchPutByID(ExecState*, JSValue, Structure*, const Identifier&, const PutPropertySlot&, StructureStubInfo&, PutKind); > void buildPutByIdList(ExecState*, JSValue, Structure*, const Identifier&, const PutPropertySlot&, StructureStubInfo&, PutKind); > void repatchIn(ExecState*, JSCell*, const Identifier&, bool wasFound, const PropertySlot&, StructureStubInfo&); >+void repatchInstanceOf(ExecState*, JSValue value, JSValue prototype, StructureStubInfo&, bool wasFound); > void linkFor(ExecState*, CallLinkInfo&, CodeBlock*, JSObject* callee, MacroAssemblerCodePtr<JSEntryPtrTag>); > void linkDirectFor(ExecState*, CallLinkInfo&, CodeBlock*, MacroAssemblerCodePtr<JSEntryPtrTag>); > void linkSlowFor(ExecState*, CallLinkInfo&); >@@ -53,7 +54,8 @@ void linkVirtualFor(ExecState*, CallLink > void linkPolymorphicCall(ExecState*, CallLinkInfo&, CallVariant); > void resetGetByID(CodeBlock*, StructureStubInfo&, GetByIDKind); > void resetPutByID(CodeBlock*, StructureStubInfo&); >-void resetIn(CodeBlock*, StructureStubInfo&); >+void resetIn(StructureStubInfo&); >+void resetInstanceOf(StructureStubInfo&); > void ftlThunkAwareRepatchCall(CodeBlock*, CodeLocationCall<JSInternalPtrTag>, FunctionPtr<CFunctionPtrTag> newCalleeFunction); > > } // namespace JSC >Index: Source/JavaScriptCore/runtime/JSCellInlines.h >=================================================================== >--- Source/JavaScriptCore/runtime/JSCellInlines.h (revision 231917) >+++ Source/JavaScriptCore/runtime/JSCellInlines.h (working copy) >@@ -215,7 +215,7 @@ inline bool JSCell::isCustomGetterSetter > > inline bool JSCell::isProxy() const > { >- return m_type == ImpureProxyType || m_type == PureForwardingProxyType; >+ return m_type == ImpureProxyType || m_type == PureForwardingProxyType || m_type == ProxyObjectType; > } > > ALWAYS_INLINE bool JSCell::isFunction(VM& vm) >Index: Source/JavaScriptCore/runtime/Structure.h >=================================================================== >--- Source/JavaScriptCore/runtime/Structure.h (revision 231917) >+++ Source/JavaScriptCore/runtime/Structure.h (working copy) >@@ -178,7 +178,7 @@ public: > bool isProxy() const > { > JSType type = m_blob.type(); >- return type == ImpureProxyType || type == PureForwardingProxyType; >+ return type == ImpureProxyType || type == PureForwardingProxyType || type == ProxyObjectType; > } > > static void dumpStatistics(); >@@ -218,10 +218,15 @@ public: > bool isDictionary() const { return dictionaryKind() != NoneDictionaryKind; } > bool isUncacheableDictionary() const { return dictionaryKind() == UncachedDictionaryKind; } > >- bool propertyAccessesAreCacheable() >+ bool prototypeQueriesAreCacheable() > { > return dictionaryKind() != UncachedDictionaryKind >- && !typeInfo().prohibitsPropertyCaching() >+ && !typeInfo().prohibitsPropertyCaching(); >+ } >+ >+ bool propertyAccessesAreCacheable() >+ { >+ return prototypeQueriesAreCacheable() > && !(typeInfo().getOwnPropertySlotIsImpure() && !typeInfo().newImpurePropertyFiresWatchpoints()); > } >
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Flags:
saam
:
review+
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 185652
:
340417
|
340419
|
340440
|
340446
|
340499
|
340511
|
340512
|
340537
|
340548
|
340549
|
340551
|
340555
|
340556
|
340573
|
340574
|
340576
|
340580
|
340585
|
340593
|
340616
|
340636
|
340649
|
340665
|
340703
|
340704