зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1740737 - Part 2: Handle arguments in optimised spread calls. r=iain
When the `arguments` object is still in its initial state (no overridden elements, length, or iterator), `...arguments` can be optimised to directly create the spread array from the arguments' content instead of going through the iterator code. The CacheIR code additionally disallows overridden or forwarded arguments in order to further optimise this operation during scalar replacement. See part 4. Differential Revision: https://phabricator.services.mozilla.com/D130987
This commit is contained in:
Родитель
1375b8c104
Коммит
109673303b
|
@ -0,0 +1,14 @@
|
|||
function foo() {
|
||||
return bar(arguments);
|
||||
}
|
||||
function bar(argsFromFoo) {
|
||||
return baz(...argsFromFoo)
|
||||
}
|
||||
function baz(a,b) { return a + b; }
|
||||
|
||||
var sum = 0;
|
||||
with ({}) {}
|
||||
for (var i = 0; i < 100; i++) {
|
||||
sum += foo(1,2);
|
||||
}
|
||||
assertEq(sum, 300);
|
|
@ -0,0 +1,18 @@
|
|||
function foo(x,y) {
|
||||
function capture() { return x; }
|
||||
return bar(arguments);
|
||||
}
|
||||
|
||||
function bar(x) {
|
||||
return baz(...x) + arguments.length;
|
||||
}
|
||||
|
||||
function baz(x,y) {
|
||||
return x + y;
|
||||
}
|
||||
|
||||
var sum = 0;
|
||||
for (var i = 0; i < 100; i++) {
|
||||
sum += foo(1,2);
|
||||
}
|
||||
assertEq(sum, 400)
|
|
@ -0,0 +1,17 @@
|
|||
function foo(x,y) {
|
||||
return bar(arguments);
|
||||
}
|
||||
|
||||
function bar(x) {
|
||||
return baz(...x) + arguments.length;
|
||||
}
|
||||
|
||||
function baz(x,y) {
|
||||
return x + y;
|
||||
}
|
||||
|
||||
var sum = 0;
|
||||
for (var i = 0; i < 100; i++) {
|
||||
sum += foo(1,2);
|
||||
}
|
||||
assertEq(sum, 400)
|
|
@ -0,0 +1,50 @@
|
|||
// |jit-test| --fast-warmup
|
||||
|
||||
function foo(...args) {
|
||||
with ({}) {}
|
||||
return {result: args.length};
|
||||
}
|
||||
|
||||
function inner() {
|
||||
return arguments;
|
||||
}
|
||||
|
||||
function outer0() {
|
||||
trialInline();
|
||||
var args = inner();
|
||||
return new foo(...args);
|
||||
}
|
||||
|
||||
function outer1() {
|
||||
trialInline();
|
||||
var args = inner(1);
|
||||
return new foo(...args);
|
||||
}
|
||||
|
||||
function outer2() {
|
||||
trialInline();
|
||||
var args = inner(1,2);
|
||||
return new foo(...args);
|
||||
}
|
||||
|
||||
function outer3() {
|
||||
trialInline();
|
||||
var args = inner(1,2,3);
|
||||
return new foo(...args);
|
||||
}
|
||||
|
||||
function outer4() {
|
||||
trialInline();
|
||||
var args = inner(1,2,3,4);
|
||||
return new foo(...args);
|
||||
}
|
||||
|
||||
with ({}) {}
|
||||
|
||||
for (var i = 0; i < 50; i++) {
|
||||
assertEq(outer0().result, 0);
|
||||
assertEq(outer1().result, 1);
|
||||
assertEq(outer2().result, 2);
|
||||
assertEq(outer3().result, 3);
|
||||
assertEq(outer4().result, 4);
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
// |jit-test| --fast-warmup
|
||||
|
||||
function foo(...args) {
|
||||
with ({}) {}
|
||||
return args.length;
|
||||
}
|
||||
|
||||
function inner() {
|
||||
return arguments;
|
||||
}
|
||||
|
||||
function outer0() {
|
||||
trialInline();
|
||||
var args = inner();
|
||||
return foo(...args);
|
||||
}
|
||||
|
||||
function outer1() {
|
||||
trialInline();
|
||||
var args = inner(1);
|
||||
return foo(...args);
|
||||
}
|
||||
|
||||
function outer2() {
|
||||
trialInline();
|
||||
var args = inner(1,2);
|
||||
return foo(...args);
|
||||
}
|
||||
|
||||
function outer3() {
|
||||
trialInline();
|
||||
var args = inner(1,2,3);
|
||||
return foo(...args);
|
||||
}
|
||||
|
||||
function outer4() {
|
||||
trialInline();
|
||||
var args = inner(1,2,3,4);
|
||||
return foo(...args);
|
||||
}
|
||||
|
||||
with ({}) {}
|
||||
|
||||
for (var i = 0; i < 50; i++) {
|
||||
assertEq(outer0(), 0);
|
||||
assertEq(outer1(), 1);
|
||||
assertEq(outer2(), 2);
|
||||
assertEq(outer3(), 3);
|
||||
assertEq(outer4(), 4);
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
// |jit-test| --fast-warmup
|
||||
|
||||
function foo(a,b) {
|
||||
with ({}) {}
|
||||
return {result: a + b};
|
||||
}
|
||||
|
||||
function inner(x,y) {
|
||||
assertEq(x + y, new foo(...arguments).result);
|
||||
}
|
||||
|
||||
function outer0() {
|
||||
trialInline();
|
||||
inner();
|
||||
}
|
||||
|
||||
function outer1() {
|
||||
trialInline();
|
||||
inner(1);
|
||||
}
|
||||
|
||||
function outer2() {
|
||||
trialInline();
|
||||
inner(1, 2);
|
||||
}
|
||||
|
||||
function outer3() {
|
||||
trialInline();
|
||||
inner(1,2,3);
|
||||
}
|
||||
|
||||
with ({}) {}
|
||||
|
||||
for (var i = 0; i < 50; i++) {
|
||||
outer0();
|
||||
outer1();
|
||||
outer2();
|
||||
outer3();
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
// |jit-test| --fast-warmup
|
||||
|
||||
|
||||
function foo(...args) {
|
||||
with ({}) {}
|
||||
return {result: args.length};
|
||||
}
|
||||
|
||||
function inner() {
|
||||
// Single argument spread calls with |arguments| can be optimised.
|
||||
return new foo(...arguments);
|
||||
}
|
||||
|
||||
function outer0() {
|
||||
trialInline();
|
||||
return inner();
|
||||
}
|
||||
|
||||
function outer1() {
|
||||
trialInline();
|
||||
return inner(1);
|
||||
}
|
||||
|
||||
function outer2() {
|
||||
trialInline();
|
||||
return inner(1, 2);
|
||||
}
|
||||
|
||||
function outer3() {
|
||||
trialInline();
|
||||
return inner(1,2,3)
|
||||
}
|
||||
|
||||
function outer4() {
|
||||
trialInline();
|
||||
return inner(1,2,3,4)
|
||||
}
|
||||
|
||||
with ({}) {}
|
||||
|
||||
for (var i = 0; i < 50; i++) {
|
||||
assertEq(outer0().result, 0);
|
||||
assertEq(outer1().result, 1);
|
||||
assertEq(outer2().result, 2);
|
||||
assertEq(outer3().result, 3);
|
||||
assertEq(outer4().result, 4);
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
// |jit-test| --fast-warmup
|
||||
|
||||
function foo(a,b) {
|
||||
with ({}) {}
|
||||
return a + b;
|
||||
}
|
||||
|
||||
function inner(x,y) {
|
||||
assertEq(x + y, foo(...arguments));
|
||||
}
|
||||
|
||||
function outer0() {
|
||||
trialInline();
|
||||
inner();
|
||||
}
|
||||
|
||||
function outer1() {
|
||||
trialInline();
|
||||
inner(1);
|
||||
}
|
||||
|
||||
function outer2() {
|
||||
trialInline();
|
||||
inner(1, 2);
|
||||
}
|
||||
|
||||
function outer3() {
|
||||
trialInline();
|
||||
inner(1,2,3);
|
||||
}
|
||||
|
||||
with ({}) {}
|
||||
|
||||
for (var i = 0; i < 50; i++) {
|
||||
outer0();
|
||||
outer1();
|
||||
outer2();
|
||||
outer3();
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
// |jit-test| --fast-warmup
|
||||
|
||||
|
||||
function foo(...args) {
|
||||
with ({}) {}
|
||||
return args.length;
|
||||
}
|
||||
|
||||
function inner() {
|
||||
// Single argument spread calls with |arguments| can be optimised.
|
||||
return foo(...arguments);
|
||||
}
|
||||
|
||||
function outer0() {
|
||||
trialInline();
|
||||
return inner();
|
||||
}
|
||||
|
||||
function outer1() {
|
||||
trialInline();
|
||||
return inner(1);
|
||||
}
|
||||
|
||||
function outer2() {
|
||||
trialInline();
|
||||
return inner(1, 2);
|
||||
}
|
||||
|
||||
function outer3() {
|
||||
trialInline();
|
||||
return inner(1,2,3)
|
||||
}
|
||||
|
||||
function outer4() {
|
||||
trialInline();
|
||||
return inner(1,2,3,4)
|
||||
}
|
||||
|
||||
with ({}) {}
|
||||
|
||||
for (var i = 0; i < 50; i++) {
|
||||
assertEq(outer0(), 0);
|
||||
assertEq(outer1(), 1);
|
||||
assertEq(outer2(), 2);
|
||||
assertEq(outer3(), 3);
|
||||
assertEq(outer4(), 4);
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
function escape(x) { with ({}) {} }
|
||||
|
||||
function foo() {
|
||||
escape(arguments);
|
||||
return bar(...arguments);
|
||||
}
|
||||
|
||||
function bar(x,y) {
|
||||
return x + y;
|
||||
}
|
||||
|
||||
with ({}) {}
|
||||
|
||||
var sum = 0;
|
||||
for (var i = 0; i < 100; i++) {
|
||||
sum += foo(1,2);
|
||||
}
|
||||
assertEq(sum, 300);
|
|
@ -0,0 +1,16 @@
|
|||
function foo() {
|
||||
arguments[0] = 3;
|
||||
return bar(...arguments);
|
||||
}
|
||||
|
||||
function bar(x,y) {
|
||||
return x + y;
|
||||
}
|
||||
|
||||
with ({}) {}
|
||||
|
||||
var sum = 0;
|
||||
for (var i = 0; i < 100; i++) {
|
||||
sum += foo(1,2);
|
||||
}
|
||||
assertEq(sum, 500);
|
|
@ -0,0 +1,20 @@
|
|||
function escape() { with ({}) {} }
|
||||
|
||||
function foo(i) {
|
||||
return i;
|
||||
}
|
||||
|
||||
function bar(n) {
|
||||
escape(arguments);
|
||||
return foo(...arguments);
|
||||
}
|
||||
|
||||
function baz(a, n) {
|
||||
return bar(n);
|
||||
}
|
||||
|
||||
var sum = 0;
|
||||
for (var i = 0; i < 10000; i++) {
|
||||
sum += baz(0, 1);
|
||||
}
|
||||
assertEq(sum, 10000);
|
|
@ -0,0 +1,21 @@
|
|||
var result;
|
||||
|
||||
function g(a, b) {
|
||||
with ({}) {}
|
||||
result = a + b;
|
||||
}
|
||||
|
||||
function escape() { with({}) {} }
|
||||
|
||||
function f() {
|
||||
escape(arguments);
|
||||
for (var i = 0; i < 50; ++i) {
|
||||
g(...arguments);
|
||||
}
|
||||
}
|
||||
|
||||
f(1, 2);
|
||||
assertEq(result, 3);
|
||||
|
||||
f("");
|
||||
assertEq(result, "undefined");
|
|
@ -0,0 +1,216 @@
|
|||
// Tests when |arguments| are used in optimized spread calls.
|
||||
|
||||
function testBasic() {
|
||||
// Return the number of arguments.
|
||||
function argslen() { return arguments.length; }
|
||||
|
||||
// Return the first argument.
|
||||
function arg0() { return arguments[0]; }
|
||||
|
||||
// Return the argument at the request index.
|
||||
function argIndex(i) { return arguments[i]; }
|
||||
|
||||
// Call the above functions when no formals are present.
|
||||
function noFormalsLen() { return argslen(...arguments); }
|
||||
function noFormals0() { return arg0(...arguments); }
|
||||
function noFormalsIndex() { return argIndex(...arguments); }
|
||||
|
||||
// Call the above functions when some formals are present.
|
||||
function formalsLen(x, y, z) { return argslen(...arguments); }
|
||||
function formals0(x, y, z) { return arg0(...arguments); }
|
||||
function formalsIndex(x, y, z) { return argIndex(...arguments); }
|
||||
|
||||
// Call the above functions when a rest argument is present.
|
||||
function restLen(...rest) { return argslen(...arguments); }
|
||||
function rest0(...rest) { return arg0(...arguments); }
|
||||
function restIndex(...rest) { return argIndex(...arguments); }
|
||||
|
||||
// Call the above functions when default parameters are present.
|
||||
function defaultLen(x = 0) { return argslen(...arguments); }
|
||||
function default0(x = 0) { return arg0(...arguments); }
|
||||
function defaultIndex(x = 0) { return argIndex(...arguments); }
|
||||
|
||||
for (let i = 0; i < 100; ++i) {
|
||||
assertEq(noFormalsLen(), 0);
|
||||
assertEq(noFormalsLen(1), 1);
|
||||
assertEq(noFormalsLen(1, 2, 3), 3);
|
||||
|
||||
assertEq(formalsLen(), 0);
|
||||
assertEq(formalsLen(1), 1);
|
||||
assertEq(formalsLen(1, 2, 3), 3);
|
||||
|
||||
assertEq(restLen(), 0);
|
||||
assertEq(restLen(1), 1);
|
||||
assertEq(restLen(1, 2, 3), 3);
|
||||
|
||||
assertEq(defaultLen(), 0);
|
||||
assertEq(defaultLen(1), 1);
|
||||
assertEq(defaultLen(1, 2, 3), 3);
|
||||
|
||||
assertEq(noFormals0(), undefined);
|
||||
assertEq(noFormals0(100), 100);
|
||||
assertEq(noFormals0(100, 200, 300), 100);
|
||||
|
||||
assertEq(formals0(), undefined);
|
||||
assertEq(formals0(100), 100);
|
||||
assertEq(formals0(100, 200, 300), 100);
|
||||
|
||||
assertEq(rest0(), undefined);
|
||||
assertEq(rest0(100), 100);
|
||||
assertEq(rest0(100, 200, 300), 100);
|
||||
|
||||
assertEq(default0(), undefined);
|
||||
assertEq(default0(100), 100);
|
||||
assertEq(default0(100, 200, 300), 100);
|
||||
|
||||
assertEq(noFormalsIndex(), undefined);
|
||||
assertEq(noFormalsIndex(0), 0);
|
||||
assertEq(noFormalsIndex(0, 100), 0);
|
||||
assertEq(noFormalsIndex(0, 100, 200, 300), 0);
|
||||
assertEq(noFormalsIndex(1, 100), 100);
|
||||
assertEq(noFormalsIndex(1, 100, 200, 300), 100);
|
||||
|
||||
assertEq(formalsIndex(), undefined);
|
||||
assertEq(formalsIndex(0), 0);
|
||||
assertEq(formalsIndex(0, 100), 0);
|
||||
assertEq(formalsIndex(0, 100, 200, 300), 0);
|
||||
assertEq(formalsIndex(1, 100), 100);
|
||||
assertEq(formalsIndex(1, 100, 200, 300), 100);
|
||||
|
||||
assertEq(restIndex(), undefined);
|
||||
assertEq(restIndex(0), 0);
|
||||
assertEq(restIndex(0, 100), 0);
|
||||
assertEq(restIndex(0, 100, 200, 300), 0);
|
||||
assertEq(restIndex(1, 100), 100);
|
||||
assertEq(restIndex(1, 100, 200, 300), 100);
|
||||
|
||||
assertEq(defaultIndex(), undefined);
|
||||
assertEq(defaultIndex(0), 0);
|
||||
assertEq(defaultIndex(0, 100), 0);
|
||||
assertEq(defaultIndex(0, 100, 200, 300), 0);
|
||||
assertEq(defaultIndex(1, 100), 100);
|
||||
assertEq(defaultIndex(1, 100, 200, 300), 100);
|
||||
}
|
||||
}
|
||||
//testBasic();
|
||||
|
||||
function testOverriddenIterator() {
|
||||
function g(x) {
|
||||
return x;
|
||||
}
|
||||
|
||||
function f(i) {
|
||||
if (i === 100) {
|
||||
arguments[Symbol.iterator] = function() {
|
||||
return ["bad"].values();
|
||||
};
|
||||
}
|
||||
return g(...arguments);
|
||||
}
|
||||
|
||||
for (let i = 0; i <= 150; ++i) {
|
||||
assertEq(f(i), i !== 100 ? i : "bad");
|
||||
}
|
||||
}
|
||||
testOverriddenIterator();
|
||||
|
||||
function testOverriddenLength() {
|
||||
function g(x, y = 0) {
|
||||
return x + y;
|
||||
}
|
||||
|
||||
function f(i) {
|
||||
if (i === 100) {
|
||||
arguments.length = 1;
|
||||
}
|
||||
return g(...arguments);
|
||||
}
|
||||
|
||||
for (let i = 0; i <= 150; ++i) {
|
||||
assertEq(f(i, i), i !== 100 ? i * 2 : i);
|
||||
}
|
||||
}
|
||||
testOverriddenLength();
|
||||
|
||||
function testOverriddenElement() {
|
||||
function g(x, y) {
|
||||
return x + y;
|
||||
}
|
||||
|
||||
function f(i) {
|
||||
if (i === 100) {
|
||||
arguments[1] = 0;
|
||||
}
|
||||
return g(...arguments);
|
||||
}
|
||||
|
||||
for (let i = 0; i <= 150; ++i) {
|
||||
assertEq(f(i, i), i !== 100 ? i * 2 : i);
|
||||
}
|
||||
}
|
||||
testOverriddenElement();
|
||||
|
||||
function testDeletedElement() {
|
||||
function g(x, y = 0) {
|
||||
return x + y;
|
||||
}
|
||||
|
||||
function f(i) {
|
||||
if (i === 100) {
|
||||
delete arguments[1];
|
||||
}
|
||||
return g(...arguments);
|
||||
}
|
||||
|
||||
for (let i = 0; i <= 150; ++i) {
|
||||
assertEq(f(i, i), i !== 100 ? i * 2 : i);
|
||||
}
|
||||
}
|
||||
testDeletedElement();
|
||||
|
||||
function testForwardedArg() {
|
||||
function g(x, y) {
|
||||
return x + y;
|
||||
}
|
||||
|
||||
function f(i) {
|
||||
function closedOver() {
|
||||
if (i === 100) i = 0;
|
||||
}
|
||||
closedOver();
|
||||
return g(...arguments);
|
||||
}
|
||||
|
||||
for (let i = 0; i <= 150; ++i) {
|
||||
assertEq(f(i, i), i !== 100 ? i * 2 : i);
|
||||
}
|
||||
}
|
||||
testForwardedArg();
|
||||
|
||||
function testModifiedArrayIteratorPrototype() {
|
||||
function g(x, y) {
|
||||
return x + y;
|
||||
}
|
||||
|
||||
const ArrayIteratorPrototype = Reflect.getPrototypeOf([][Symbol.iterator]());
|
||||
const ArrayIteratorPrototypeNext = ArrayIteratorPrototype.next;
|
||||
function newNext() {
|
||||
var result = ArrayIteratorPrototypeNext.call(this);
|
||||
if (!result.done) {
|
||||
result.value *= 2;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function f(i) {
|
||||
if (i === 100) {
|
||||
ArrayIteratorPrototype.next = newNext;
|
||||
}
|
||||
return g(...arguments);
|
||||
}
|
||||
|
||||
for (let i = 0; i <= 150; ++i) {
|
||||
assertEq(f(i, i), i < 100 ? i * 2 : i * 4);
|
||||
}
|
||||
}
|
||||
testModifiedArrayIteratorPrototype();
|
|
@ -0,0 +1,15 @@
|
|||
'use strict'
|
||||
function bar(x,y) {
|
||||
return x + y;
|
||||
}
|
||||
|
||||
function foo(x, y) {
|
||||
function closeOver() { return x; }
|
||||
return bar(...arguments);
|
||||
}
|
||||
|
||||
var sum = 0;
|
||||
for (var i = 0; i < 100; i++) {
|
||||
sum += foo(1,2);
|
||||
}
|
||||
assertEq(sum, 300)
|
|
@ -0,0 +1,14 @@
|
|||
function bar(x,y) {
|
||||
return x + y;
|
||||
}
|
||||
|
||||
function foo(x, y) {
|
||||
function closeOver() { return x; }
|
||||
return bar(...arguments);
|
||||
}
|
||||
|
||||
var sum = 0;
|
||||
for (var i = 0; i < 100; i++) {
|
||||
sum += foo(1,2);
|
||||
}
|
||||
assertEq(sum, 300)
|
|
@ -0,0 +1,18 @@
|
|||
function foo() {
|
||||
arguments.length = 2;
|
||||
return bar(...arguments);
|
||||
}
|
||||
|
||||
function bar() {
|
||||
var result = 0;
|
||||
for (var x of arguments) {
|
||||
result += x;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
var sum = 0;
|
||||
for (var i = 0; i < 100; i++) {
|
||||
sum += foo(1,2,3,4,5,6);
|
||||
}
|
||||
assertEq(sum,300);
|
|
@ -4937,6 +4937,7 @@ AttachDecision OptimizeSpreadCallIRGenerator::tryAttachStub() {
|
|||
AutoAssertNoPendingException aanpe(cx_);
|
||||
|
||||
TRY_ATTACH(tryAttachArray());
|
||||
TRY_ATTACH(tryAttachArguments());
|
||||
TRY_ATTACH(tryAttachNotOptimizable());
|
||||
|
||||
trackAttached(IRGenerator::NotAttached);
|
||||
|
@ -5065,6 +5066,63 @@ AttachDecision OptimizeSpreadCallIRGenerator::tryAttachArray() {
|
|||
return AttachDecision::Attach;
|
||||
}
|
||||
|
||||
AttachDecision OptimizeSpreadCallIRGenerator::tryAttachArguments() {
|
||||
// The value must be an arguments object.
|
||||
if (!val_.isObject()) {
|
||||
return AttachDecision::NoAction;
|
||||
}
|
||||
JSObject* obj = &val_.toObject();
|
||||
if (!obj->is<ArgumentsObject>()) {
|
||||
return AttachDecision::NoAction;
|
||||
}
|
||||
auto* args = &obj->as<ArgumentsObject>();
|
||||
|
||||
// Ensure neither elements, nor the length, nor the iterator has been
|
||||
// overridden. Also ensure no args are forwarded to allow reading them
|
||||
// directly from the frame.
|
||||
if (args->hasOverriddenElement() || args->hasOverriddenLength() ||
|
||||
args->hasOverriddenIterator() || args->anyArgIsForwarded()) {
|
||||
return AttachDecision::NoAction;
|
||||
}
|
||||
|
||||
NativeObject* arrayIteratorProto;
|
||||
uint32_t slot;
|
||||
JSFunction* nextFun;
|
||||
if (!IsArrayIteratorPrototypeOptimizable(cx_, &arrayIteratorProto, &slot,
|
||||
&nextFun)) {
|
||||
return AttachDecision::NoAction;
|
||||
}
|
||||
|
||||
ValOperandId valId(writer.setInputOperandId(0));
|
||||
ObjOperandId objId = writer.guardToObject(valId);
|
||||
|
||||
if (args->is<MappedArgumentsObject>()) {
|
||||
writer.guardClass(objId, GuardClassKind::MappedArguments);
|
||||
} else {
|
||||
MOZ_ASSERT(args->is<UnmappedArgumentsObject>());
|
||||
writer.guardClass(objId, GuardClassKind::UnmappedArguments);
|
||||
}
|
||||
uint8_t flags = ArgumentsObject::ELEMENT_OVERRIDDEN_BIT |
|
||||
ArgumentsObject::LENGTH_OVERRIDDEN_BIT |
|
||||
ArgumentsObject::ITERATOR_OVERRIDDEN_BIT |
|
||||
ArgumentsObject::FORWARDED_ARGUMENTS_BIT;
|
||||
writer.guardArgumentsObjectFlags(objId, flags);
|
||||
|
||||
ObjOperandId protoId = writer.loadObject(arrayIteratorProto);
|
||||
ObjOperandId nextId = writer.loadObject(nextFun);
|
||||
|
||||
writer.guardShape(protoId, arrayIteratorProto->shape());
|
||||
|
||||
// Ensure that proto[slot] == nextFun.
|
||||
writer.guardDynamicSlotIsSpecificObject(protoId, nextId, slot);
|
||||
|
||||
writer.arrayFromArgumentsObjectResult(objId);
|
||||
writer.returnFromIC();
|
||||
|
||||
trackAttached("Arguments");
|
||||
return AttachDecision::Attach;
|
||||
}
|
||||
|
||||
AttachDecision OptimizeSpreadCallIRGenerator::tryAttachNotOptimizable() {
|
||||
ValOperandId valId(writer.setInputOperandId(0));
|
||||
|
||||
|
|
|
@ -1619,6 +1619,7 @@ class MOZ_RAII OptimizeSpreadCallIRGenerator : public IRGenerator {
|
|||
HandleValue val_;
|
||||
|
||||
AttachDecision tryAttachArray();
|
||||
AttachDecision tryAttachArguments();
|
||||
AttachDecision tryAttachNotOptimizable();
|
||||
|
||||
public:
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
#include "vm/FunctionFlags.h" // js::FunctionFlags
|
||||
#include "vm/GeneratorObject.h"
|
||||
#include "vm/GetterSetter.h"
|
||||
#include "vm/Interpreter.h"
|
||||
#include "vm/Uint8Clamped.h"
|
||||
|
||||
#include "builtin/Boolean-inl.h"
|
||||
|
@ -8558,6 +8559,21 @@ bool CacheIRCompiler::emitMapGetObjectResult(ObjOperandId mapId,
|
|||
return true;
|
||||
}
|
||||
|
||||
bool CacheIRCompiler::emitArrayFromArgumentsObjectResult(ObjOperandId objId) {
|
||||
JitSpew(JitSpew_Codegen, "%s", __FUNCTION__);
|
||||
|
||||
AutoCallVM callvm(masm, this, allocator);
|
||||
|
||||
Register obj = allocator.useRegister(masm, objId);
|
||||
|
||||
callvm.prepare();
|
||||
masm.Push(obj);
|
||||
|
||||
using Fn = ArrayObject* (*)(JSContext*, Handle<ArgumentsObject*>);
|
||||
callvm.call<Fn, js::ArrayFromArgumentsObject>();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CacheIRCompiler::emitBailout() {
|
||||
JitSpew(JitSpew_Codegen, "%s", __FUNCTION__);
|
||||
|
||||
|
|
|
@ -2780,6 +2780,13 @@
|
|||
map: ObjId
|
||||
obj: ObjId
|
||||
|
||||
- name: ArrayFromArgumentsObjectResult
|
||||
shared: true
|
||||
transpile: false
|
||||
cost_estimate: 5
|
||||
args:
|
||||
obj: ObjId
|
||||
|
||||
- name: CallPrintString
|
||||
shared: true
|
||||
transpile: false
|
||||
|
|
|
@ -40,6 +40,7 @@ namespace jit {
|
|||
js::ArgumentsObject::createForInlinedIon) \
|
||||
_(ArgumentsObjectCreateForIon, js::ArgumentsObject::createForIon) \
|
||||
_(ArrayConstructorOneArg, js::ArrayConstructorOneArg) \
|
||||
_(ArrayFromArgumentsObject, js::ArrayFromArgumentsObject) \
|
||||
_(ArrayJoin, js::jit::ArrayJoin) \
|
||||
_(ArrayPushDense, js::jit::ArrayPushDense) \
|
||||
_(ArraySliceDense, js::ArraySliceDense) \
|
||||
|
|
|
@ -66,6 +66,7 @@
|
|||
|
||||
#include "builtin/Boolean-inl.h"
|
||||
#include "debugger/DebugAPI-inl.h"
|
||||
#include "vm/ArgumentsObject-inl.h"
|
||||
#include "vm/EnvironmentObject-inl.h"
|
||||
#include "vm/GeckoProfiler-inl.h"
|
||||
#include "vm/JSAtom-inl.h"
|
||||
|
@ -4991,8 +4992,10 @@ bool js::SpreadCallOperation(JSContext* cx, HandleScript script, jsbytecode* pc,
|
|||
return true;
|
||||
}
|
||||
|
||||
bool js::OptimizeSpreadCall(JSContext* cx, HandleValue arg,
|
||||
MutableHandleValue result) {
|
||||
static bool OptimizeArraySpreadCall(JSContext* cx, HandleObject obj,
|
||||
MutableHandleValue result) {
|
||||
MOZ_ASSERT(result.isUndefined());
|
||||
|
||||
// Optimize spread call by skipping spread operation when following
|
||||
// conditions are met:
|
||||
// * the argument is an array
|
||||
|
@ -5001,14 +5004,7 @@ bool js::OptimizeSpreadCall(JSContext* cx, HandleValue arg,
|
|||
// * the array's prototype is Array.prototype
|
||||
// * Array.prototype[@@iterator] is not modified
|
||||
// * %ArrayIteratorPrototype%.next is not modified
|
||||
if (!arg.isObject()) {
|
||||
result.setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
RootedObject obj(cx, &arg.toObject());
|
||||
if (!IsPackedArray(obj)) {
|
||||
result.setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -5021,15 +5017,106 @@ bool js::OptimizeSpreadCall(JSContext* cx, HandleValue arg,
|
|||
if (!stubChain->tryOptimizeArray(cx, obj.as<ArrayObject>(), &optimized)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (optimized) {
|
||||
result.setObject(*obj);
|
||||
} else {
|
||||
result.setUndefined();
|
||||
if (!optimized) {
|
||||
return true;
|
||||
}
|
||||
|
||||
result.setObject(*obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool OptimizeArgumentsSpreadCall(JSContext* cx, HandleObject obj,
|
||||
MutableHandleValue result) {
|
||||
MOZ_ASSERT(result.isUndefined());
|
||||
|
||||
// Optimize spread call by skipping the spread operation when the following
|
||||
// conditions are met:
|
||||
// * the argument is an arguments object
|
||||
// * the arguments object has no deleted elements
|
||||
// * arguments.length is not overridden
|
||||
// * arguments[@@iterator] is not overridden
|
||||
// * %ArrayIteratorPrototype%.next is not modified
|
||||
|
||||
if (!obj->is<ArgumentsObject>()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Handle<ArgumentsObject*> args = obj.as<ArgumentsObject>();
|
||||
if (args->isAnyElementDeleted() || args->hasOverriddenLength() ||
|
||||
args->hasOverriddenIterator()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
ForOfPIC::Chain* stubChain = ForOfPIC::getOrCreate(cx);
|
||||
if (!stubChain) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool optimized;
|
||||
if (!stubChain->tryOptimizeArrayIteratorNext(cx, &optimized)) {
|
||||
return false;
|
||||
}
|
||||
if (!optimized) {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto* array = ArrayFromArgumentsObject(cx, args);
|
||||
if (!array) {
|
||||
return false;
|
||||
}
|
||||
|
||||
result.setObject(*array);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool js::OptimizeSpreadCall(JSContext* cx, HandleValue arg,
|
||||
MutableHandleValue result) {
|
||||
// This function returns |undefined| if the spread operation can't be
|
||||
// optimized.
|
||||
result.setUndefined();
|
||||
|
||||
if (!arg.isObject()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
RootedObject obj(cx, &arg.toObject());
|
||||
if (!OptimizeArraySpreadCall(cx, obj, result)) {
|
||||
return false;
|
||||
}
|
||||
if (result.isObject()) {
|
||||
return true;
|
||||
}
|
||||
if (!OptimizeArgumentsSpreadCall(cx, obj, result)) {
|
||||
return false;
|
||||
}
|
||||
if (result.isObject()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(result.isUndefined());
|
||||
return true;
|
||||
}
|
||||
|
||||
ArrayObject* js::ArrayFromArgumentsObject(JSContext* cx,
|
||||
Handle<ArgumentsObject*> args) {
|
||||
MOZ_ASSERT(!args->hasOverriddenLength());
|
||||
MOZ_ASSERT(!args->isAnyElementDeleted());
|
||||
|
||||
uint32_t length = args->initialLength();
|
||||
auto* array = NewDenseFullyAllocatedArray(cx, length);
|
||||
if (!array) {
|
||||
return nullptr;
|
||||
}
|
||||
array->setDenseInitializedLength(length);
|
||||
|
||||
for (uint32_t index = 0; index < length; index++) {
|
||||
const Value& v = args->element(index);
|
||||
array->initDenseElement(index, v);
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
JSObject* js::NewObjectOperation(JSContext* cx, HandleScript script,
|
||||
const jsbytecode* pc) {
|
||||
if (JSOp(*pc) == JSOp::NewObject) {
|
||||
|
|
|
@ -616,6 +616,9 @@ bool SpreadCallOperation(JSContext* cx, HandleScript script, jsbytecode* pc,
|
|||
bool OptimizeSpreadCall(JSContext* cx, HandleValue arg,
|
||||
MutableHandleValue result);
|
||||
|
||||
ArrayObject* ArrayFromArgumentsObject(JSContext* cx,
|
||||
Handle<ArgumentsObject*> args);
|
||||
|
||||
JSObject* NewObjectOperation(JSContext* cx, HandleScript script,
|
||||
const jsbytecode* pc);
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче