зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1108290 - optimize apply with Array. r=nbp
--HG-- extra : rebase_source : b64fc074fcdafbfa68e1e0c1a1eb7ddce57bb171
This commit is contained in:
Родитель
4a082e1689
Коммит
086b3ba3e9
|
@ -0,0 +1,98 @@
|
|||
// Test inlining in Ion of fun.apply(..., array).
|
||||
|
||||
if (!this.getJitCompilerOptions() || !getJitCompilerOptions()['ion.enable'])
|
||||
quit(0);
|
||||
|
||||
var itercount = 1000;
|
||||
var warmup = 100;
|
||||
|
||||
// Force Ion to do something predictable without having to wait
|
||||
// forever for it.
|
||||
|
||||
if (getJitCompilerOptions()["ion.warmup.trigger"] > warmup)
|
||||
setJitCompilerOption("ion.warmup.trigger", warmup);
|
||||
|
||||
setJitCompilerOption("offthread-compilation.enable", 0);
|
||||
|
||||
function g(a, b, c, d) {
|
||||
return a + b + c + (d === undefined);
|
||||
}
|
||||
|
||||
var g_inIonInLoop = false;
|
||||
var g_inIonAtEnd = false;
|
||||
|
||||
function f(xs) {
|
||||
var sum = 0;
|
||||
var inIonInLoop = 0;
|
||||
for ( var i=0 ; i < itercount ; i++ ) {
|
||||
inIonInLoop |= inIon();
|
||||
sum += g.apply(null, xs);
|
||||
}
|
||||
g_ionAtEnd = inIon();
|
||||
g_inIonInLoop = !!inIonInLoop;
|
||||
return sum;
|
||||
}
|
||||
|
||||
// Basic test
|
||||
|
||||
assertEq(f([1,2,3,4]), 6*itercount);
|
||||
|
||||
// Attempt to detect a botched optimization: either we ion-compiled
|
||||
// the loop, or we did not ion-compile the function (ion not actually
|
||||
// effective at all, this can happen).
|
||||
|
||||
assertEq(g_inIonInLoop || !g_inIonAtEnd, true);
|
||||
|
||||
// If Ion is inert just leave.
|
||||
|
||||
if (!g_inIonInLoop) {
|
||||
print("Leaving early - ion not kicking in at all");
|
||||
quit(0);
|
||||
}
|
||||
|
||||
// Test that we get the correct argument value even if the array has
|
||||
// fewer initialized members than its length.
|
||||
|
||||
var headroom = [1,2,3];
|
||||
headroom.length = 13;
|
||||
assertEq(f(headroom), 7*itercount);
|
||||
|
||||
// Test that we throw when the array is too long.
|
||||
|
||||
var thrown = false;
|
||||
try {
|
||||
var long = [];
|
||||
long.length = getMaxArgs() + 1;
|
||||
f(long);
|
||||
}
|
||||
catch (e) {
|
||||
thrown = true;
|
||||
assertEq(e instanceof RangeError, true);
|
||||
}
|
||||
assertEq(thrown, true);
|
||||
|
||||
// Test that the optimization is effective. There's the possibility
|
||||
// of some false results here, and in release builds the margins are
|
||||
// actually small.
|
||||
|
||||
itercount *= 2;
|
||||
|
||||
var A = Date.now();
|
||||
assertEq(f([1,2,3,4]), 6*itercount) // Fast path because a sane array
|
||||
var AinLoop = g_inIonInLoop;
|
||||
var B = Date.now();
|
||||
assertEq(f([1,2,3]), 7*itercount); // Fast path because a sane array, even if short
|
||||
var BinLoop = g_inIonInLoop;
|
||||
var C = Date.now();
|
||||
assertEq(f(headroom), 7*itercount); // Slow path because length > initializedLength
|
||||
var CinLoop = g_inIonInLoop;
|
||||
var D = Date.now();
|
||||
if (AinLoop && BinLoop && CinLoop) {
|
||||
print("No bailout: " + (B - A));
|
||||
print("Short: " + (C - B));
|
||||
print("Bailout: " + (D - C));
|
||||
assertEq((D - C) >= (B - A), true);
|
||||
assertEq((D - C) >= (C - B), true);
|
||||
} else {
|
||||
print("Not running perf test");
|
||||
}
|
|
@ -3270,8 +3270,9 @@ CodeGenerator::visitCallKnown(LCallKnown* call)
|
|||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void
|
||||
CodeGenerator::emitCallInvokeFunction(LApplyArgsGeneric* apply, Register extraStackSize)
|
||||
CodeGenerator::emitCallInvokeFunction(T* apply, Register extraStackSize)
|
||||
{
|
||||
Register objreg = ToRegister(apply->getTempObject());
|
||||
MOZ_ASSERT(objreg != extraStackSize);
|
||||
|
@ -3294,12 +3295,8 @@ CodeGenerator::emitCallInvokeFunction(LApplyArgsGeneric* apply, Register extraSt
|
|||
// Do not bailout after the execution of this function since the stack no longer
|
||||
// correspond to what is expected by the snapshots.
|
||||
void
|
||||
CodeGenerator::emitPushArguments(LApplyArgsGeneric* apply, Register extraStackSpace)
|
||||
CodeGenerator::emitAllocateSpaceForApply(Register argcreg, Register extraStackSpace, Label* end)
|
||||
{
|
||||
// Holds the function nargs. Initially undefined.
|
||||
Register argcreg = ToRegister(apply->getArgc());
|
||||
Register copyreg = ToRegister(apply->getTempObject());
|
||||
|
||||
// Initialize the loop counter AND Compute the stack usage (if == 0)
|
||||
masm.movePtr(argcreg, extraStackSpace);
|
||||
|
||||
|
@ -3335,9 +3332,53 @@ CodeGenerator::emitPushArguments(LApplyArgsGeneric* apply, Register extraStackSp
|
|||
}
|
||||
#endif
|
||||
|
||||
// Skip the copy of arguments.
|
||||
// Skip the copy of arguments if there are none.
|
||||
masm.branchTestPtr(Assembler::Zero, argcreg, argcreg, end);
|
||||
}
|
||||
|
||||
// Destroys argvIndex and copyreg.
|
||||
void
|
||||
CodeGenerator::emitCopyValuesForApply(Register argvSrcBase, Register argvIndex, Register copyreg,
|
||||
size_t argvSrcOffset, size_t argvDstOffset)
|
||||
{
|
||||
Label loop;
|
||||
masm.bind(&loop);
|
||||
|
||||
// As argvIndex is off by 1, and we use the decBranchPtr instruction
|
||||
// to loop back, we have to substract the size of the word which are
|
||||
// copied.
|
||||
BaseValueIndex srcPtr(argvSrcBase, argvIndex, argvSrcOffset - sizeof(void*));
|
||||
BaseValueIndex dstPtr(masm.getStackPointer(), argvIndex, argvDstOffset - sizeof(void*));
|
||||
masm.loadPtr(srcPtr, copyreg);
|
||||
masm.storePtr(copyreg, dstPtr);
|
||||
|
||||
// Handle 32 bits architectures.
|
||||
if (sizeof(Value) == 2 * sizeof(void*)) {
|
||||
BaseValueIndex srcPtrLow(argvSrcBase, argvIndex, argvSrcOffset - 2 * sizeof(void*));
|
||||
BaseValueIndex dstPtrLow(masm.getStackPointer(), argvIndex, argvDstOffset - 2 * sizeof(void*));
|
||||
masm.loadPtr(srcPtrLow, copyreg);
|
||||
masm.storePtr(copyreg, dstPtrLow);
|
||||
}
|
||||
|
||||
masm.decBranchPtr(Assembler::NonZero, argvIndex, Imm32(1), &loop);
|
||||
}
|
||||
|
||||
void
|
||||
CodeGenerator::emitPopArguments(Register extraStackSpace)
|
||||
{
|
||||
// Pop |this| and Arguments.
|
||||
masm.freeStack(extraStackSpace);
|
||||
}
|
||||
|
||||
void
|
||||
CodeGenerator::emitPushArguments(LApplyArgsGeneric* apply, Register extraStackSpace)
|
||||
{
|
||||
// Holds the function nargs. Initially the number of args to the caller.
|
||||
Register argcreg = ToRegister(apply->getArgc());
|
||||
Register copyreg = ToRegister(apply->getTempObject());
|
||||
|
||||
Label end;
|
||||
masm.branchTestPtr(Assembler::Zero, argcreg, argcreg, &end);
|
||||
emitAllocateSpaceForApply(argcreg, extraStackSpace, &end);
|
||||
|
||||
// We are making a copy of the arguments which are above the JitFrameLayout
|
||||
// of the current Ion frame.
|
||||
|
@ -3365,28 +3406,7 @@ CodeGenerator::emitPushArguments(LApplyArgsGeneric* apply, Register extraStackSp
|
|||
masm.addStackPtrTo(argvSrcBase);
|
||||
|
||||
// Copy arguments.
|
||||
{
|
||||
Label loop;
|
||||
masm.bind(&loop);
|
||||
|
||||
// As argvIndex is off by 1, and we use the decBranchPtr instruction
|
||||
// to loop back, we have to substract the size of the word which are
|
||||
// copied.
|
||||
BaseValueIndex srcPtr(argvSrcBase, argvIndex, argvSrcOffset - sizeof(void*));
|
||||
BaseValueIndex dstPtr(masm.getStackPointer(), argvIndex, argvDstOffset - sizeof(void*));
|
||||
masm.loadPtr(srcPtr, copyreg);
|
||||
masm.storePtr(copyreg, dstPtr);
|
||||
|
||||
// Handle 32 bits architectures.
|
||||
if (sizeof(Value) == 2 * sizeof(void*)) {
|
||||
BaseValueIndex srcPtrLow(argvSrcBase, argvIndex, argvSrcOffset - 2 * sizeof(void*));
|
||||
BaseValueIndex dstPtrLow(masm.getStackPointer(), argvIndex, argvDstOffset - 2 * sizeof(void*));
|
||||
masm.loadPtr(srcPtrLow, copyreg);
|
||||
masm.storePtr(copyreg, dstPtrLow);
|
||||
}
|
||||
|
||||
masm.decBranchPtr(Assembler::NonZero, argvIndex, Imm32(1), &loop);
|
||||
}
|
||||
emitCopyValuesForApply(argvSrcBase, argvIndex, copyreg, argvSrcOffset, argvDstOffset);
|
||||
|
||||
// Restore argcreg and the extra stack space counter.
|
||||
masm.pop(argcreg);
|
||||
|
@ -3401,14 +3421,61 @@ CodeGenerator::emitPushArguments(LApplyArgsGeneric* apply, Register extraStackSp
|
|||
}
|
||||
|
||||
void
|
||||
CodeGenerator::emitPopArguments(LApplyArgsGeneric* apply, Register extraStackSpace)
|
||||
CodeGenerator::emitPushArguments(LApplyArrayGeneric* apply, Register extraStackSpace)
|
||||
{
|
||||
// Pop |this| and Arguments.
|
||||
masm.freeStack(extraStackSpace);
|
||||
Label noCopy, epilogue;
|
||||
Register tmpArgc = ToRegister(apply->getTempObject());
|
||||
Register elementsAndArgc = ToRegister(apply->getElements());
|
||||
|
||||
// Invariants guarded in the caller:
|
||||
// - the array is not too long
|
||||
// - the array length equals its initialized length
|
||||
|
||||
// The array length is our argc for the purposes of allocating space.
|
||||
Address length(ToRegister(apply->getElements()), ObjectElements::offsetOfLength());
|
||||
masm.load32(length, tmpArgc);
|
||||
|
||||
// Allocate space for the values.
|
||||
emitAllocateSpaceForApply(tmpArgc, extraStackSpace, &noCopy);
|
||||
|
||||
// Copy the values. This code is skipped entirely if there are
|
||||
// no values.
|
||||
size_t argvDstOffset = 0;
|
||||
|
||||
Register argvSrcBase = elementsAndArgc; // Elements value
|
||||
|
||||
masm.push(extraStackSpace);
|
||||
Register copyreg = extraStackSpace;
|
||||
argvDstOffset += sizeof(void*);
|
||||
|
||||
masm.push(tmpArgc);
|
||||
Register argvIndex = tmpArgc;
|
||||
argvDstOffset += sizeof(void*);
|
||||
|
||||
// Copy
|
||||
emitCopyValuesForApply(argvSrcBase, argvIndex, copyreg, 0, argvDstOffset);
|
||||
|
||||
// Restore.
|
||||
masm.pop(elementsAndArgc);
|
||||
masm.pop(extraStackSpace);
|
||||
masm.jump(&epilogue);
|
||||
|
||||
// Clear argc if we skipped the copy step.
|
||||
masm.bind(&noCopy);
|
||||
masm.movePtr(ImmPtr(0), elementsAndArgc);
|
||||
|
||||
// Join with all arguments copied and the extra stack usage computed.
|
||||
// Note, "elements" has become "argc".
|
||||
masm.bind(&epilogue);
|
||||
|
||||
// Push |this|.
|
||||
masm.addPtr(Imm32(sizeof(Value)), extraStackSpace);
|
||||
masm.pushValue(ToValue(apply, LApplyArgsGeneric::ThisIndex));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void
|
||||
CodeGenerator::visitApplyArgsGeneric(LApplyArgsGeneric* apply)
|
||||
CodeGenerator::emitApplyGeneric(T* apply)
|
||||
{
|
||||
// Holds the function object.
|
||||
Register calleereg = ToRegister(apply->getFunction());
|
||||
|
@ -3417,7 +3484,8 @@ CodeGenerator::visitApplyArgsGeneric(LApplyArgsGeneric* apply)
|
|||
Register objreg = ToRegister(apply->getTempObject());
|
||||
Register extraStackSpace = ToRegister(apply->getTempStackCounter());
|
||||
|
||||
// Holds the function nargs. Initially undefined.
|
||||
// Holds the function nargs, computed in the invoker or (for
|
||||
// ApplyArray) in the argument pusher.
|
||||
Register argcreg = ToRegister(apply->getArgc());
|
||||
|
||||
// Unless already known, guard that calleereg is actually a function object.
|
||||
|
@ -3429,6 +3497,15 @@ CodeGenerator::visitApplyArgsGeneric(LApplyArgsGeneric* apply)
|
|||
}
|
||||
|
||||
// Copy the arguments of the current function.
|
||||
//
|
||||
// In the case of ApplyArray, also compute argc: the argc register
|
||||
// and the elements register are the same; argc must not be
|
||||
// referenced before the call to emitPushArguments() and elements
|
||||
// must not be referenced after it returns.
|
||||
//
|
||||
// objreg is dead across this call.
|
||||
//
|
||||
// extraStackSpace is garbage on entry and defined on exit.
|
||||
emitPushArguments(apply, extraStackSpace);
|
||||
|
||||
masm.checkStackAlignment();
|
||||
|
@ -3436,7 +3513,7 @@ CodeGenerator::visitApplyArgsGeneric(LApplyArgsGeneric* apply)
|
|||
// If the function is native, only emit the call to InvokeFunction.
|
||||
if (apply->hasSingleTarget() && apply->getSingleTarget()->isNative()) {
|
||||
emitCallInvokeFunction(apply, extraStackSpace);
|
||||
emitPopArguments(apply, extraStackSpace);
|
||||
emitPopArguments(extraStackSpace);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -3518,7 +3595,31 @@ CodeGenerator::visitApplyArgsGeneric(LApplyArgsGeneric* apply)
|
|||
|
||||
// Pop arguments and continue.
|
||||
masm.bind(&end);
|
||||
emitPopArguments(apply, extraStackSpace);
|
||||
emitPopArguments(extraStackSpace);
|
||||
}
|
||||
|
||||
void
|
||||
CodeGenerator::visitApplyArgsGeneric(LApplyArgsGeneric* apply)
|
||||
{
|
||||
emitApplyGeneric(apply);
|
||||
}
|
||||
|
||||
void
|
||||
CodeGenerator::visitApplyArrayGeneric(LApplyArrayGeneric* apply)
|
||||
{
|
||||
LSnapshot* snapshot = apply->snapshot();
|
||||
Register tmp = ToRegister(apply->getTempObject());
|
||||
|
||||
Address length(ToRegister(apply->getElements()), ObjectElements::offsetOfLength());
|
||||
masm.load32(length, tmp);
|
||||
bailoutCmp32(Assembler::Above, tmp, Imm32(js_JitOptions.maxStackArgs), snapshot);
|
||||
|
||||
Address initializedLength(ToRegister(apply->getElements()),
|
||||
ObjectElements::offsetOfInitializedLength());
|
||||
masm.sub32(initializedLength, tmp);
|
||||
bailoutCmp32(Assembler::NotEqual, tmp, Imm32(0), snapshot);
|
||||
|
||||
emitApplyGeneric(apply);
|
||||
}
|
||||
|
||||
typedef bool (*ArraySpliceDenseFn)(JSContext*, HandleObject, uint32_t, uint32_t);
|
||||
|
|
|
@ -151,10 +151,16 @@ class CodeGenerator : public CodeGeneratorSpecific
|
|||
uint32_t numFormals,
|
||||
uint32_t unusedStack);
|
||||
void visitCallKnown(LCallKnown* call);
|
||||
void emitCallInvokeFunction(LApplyArgsGeneric* apply, Register extraStackSize);
|
||||
template<typename T> void emitApplyGeneric(T* apply);
|
||||
template<typename T> void emitCallInvokeFunction(T* apply, Register extraStackSize);
|
||||
void emitAllocateSpaceForApply(Register argcreg, Register extraStackSpace, Label* end);
|
||||
void emitCopyValuesForApply(Register argvSrcBase, Register argvIndex, Register copyreg,
|
||||
size_t argvSrcOffset, size_t argvDstOffset);
|
||||
void emitPopArguments(Register extraStackSize);
|
||||
void emitPushArguments(LApplyArgsGeneric* apply, Register extraStackSpace);
|
||||
void emitPopArguments(LApplyArgsGeneric* apply, Register extraStackSize);
|
||||
void visitApplyArgsGeneric(LApplyArgsGeneric* apply);
|
||||
void emitPushArguments(LApplyArrayGeneric* apply, Register extraStackSpace);
|
||||
void visitApplyArrayGeneric(LApplyArrayGeneric* apply);
|
||||
void visitBail(LBail* lir);
|
||||
void visitUnreachable(LUnreachable* unreachable);
|
||||
void visitEncodeSnapshot(LEncodeSnapshot* lir);
|
||||
|
|
|
@ -6335,6 +6335,17 @@ IonBuilder::jsop_funapply(uint32_t argc)
|
|||
|
||||
// Fallback to regular call if arg 2 is not definitely |arguments|.
|
||||
if (argument->type() != MIRType_MagicOptimizedArguments) {
|
||||
// Optimize fun.apply(self, array) if the length is sane and there are no holes.
|
||||
TemporaryTypeSet* objTypes = argument->resultTypeSet();
|
||||
if (native && native->isNative() && native->native() == fun_apply &&
|
||||
objTypes &&
|
||||
objTypes->getKnownClass(constraints()) == &ArrayObject::class_ &&
|
||||
!objTypes->hasObjectFlags(constraints(), OBJECT_FLAG_LENGTH_OVERFLOW) &&
|
||||
ElementAccessIsPacked(constraints(), argument))
|
||||
{
|
||||
return jsop_funapplyarray(argc);
|
||||
}
|
||||
|
||||
CallInfo callInfo(alloc(), false);
|
||||
if (!callInfo.init(current, argc))
|
||||
return false;
|
||||
|
@ -6352,6 +6363,43 @@ IonBuilder::jsop_funapply(uint32_t argc)
|
|||
return jsop_funapplyarguments(argc);
|
||||
}
|
||||
|
||||
bool
|
||||
IonBuilder::jsop_funapplyarray(uint32_t argc)
|
||||
{
|
||||
MOZ_ASSERT(argc == 2);
|
||||
|
||||
int funcDepth = -((int)argc + 1);
|
||||
|
||||
// Extract call target.
|
||||
TemporaryTypeSet* funTypes = current->peek(funcDepth)->resultTypeSet();
|
||||
JSFunction* target = getSingleCallTarget(funTypes);
|
||||
|
||||
// Pop the array agument
|
||||
MDefinition* argObj = current->pop();
|
||||
|
||||
MElements* elements = MElements::New(alloc(), argObj);
|
||||
current->add(elements);
|
||||
|
||||
// Pop the |this| argument.
|
||||
MDefinition* argThis = current->pop();
|
||||
|
||||
// Unwrap the (JSFunction *) parameter.
|
||||
MDefinition* argFunc = current->pop();
|
||||
|
||||
// Pop apply function.
|
||||
MDefinition* nativeFunc = current->pop();
|
||||
nativeFunc->setImplicitlyUsedUnchecked();
|
||||
|
||||
MApplyArray* apply = MApplyArray::New(alloc(), target, argFunc, elements, argThis);
|
||||
current->add(apply);
|
||||
current->push(apply);
|
||||
if (!resumeAfter(apply))
|
||||
return false;
|
||||
|
||||
TemporaryTypeSet* types = bytecodeTypes(pc);
|
||||
return pushTypeBarrier(apply, types, BarrierKind::TypeSet);
|
||||
}
|
||||
|
||||
bool
|
||||
IonBuilder::jsop_funapplyarguments(uint32_t argc)
|
||||
{
|
||||
|
|
|
@ -666,6 +666,7 @@ class IonBuilder
|
|||
bool jsop_funcall(uint32_t argc);
|
||||
bool jsop_funapply(uint32_t argc);
|
||||
bool jsop_funapplyarguments(uint32_t argc);
|
||||
bool jsop_funapplyarray(uint32_t argc);
|
||||
bool jsop_call(uint32_t argc, bool constructing);
|
||||
bool jsop_eval(uint32_t argc);
|
||||
bool jsop_ifeq(JSOp op);
|
||||
|
|
|
@ -525,6 +525,38 @@ LIRGenerator::visitApplyArgs(MApplyArgs* apply)
|
|||
assignSafepoint(lir, apply);
|
||||
}
|
||||
|
||||
void
|
||||
LIRGenerator::visitApplyArray(MApplyArray* apply)
|
||||
{
|
||||
MOZ_ASSERT(apply->getFunction()->type() == MIRType_Object);
|
||||
|
||||
// Assert if we cannot build a rectifier frame.
|
||||
MOZ_ASSERT(CallTempReg0 != ArgumentsRectifierReg);
|
||||
MOZ_ASSERT(CallTempReg1 != ArgumentsRectifierReg);
|
||||
|
||||
// Assert if the return value is already erased.
|
||||
MOZ_ASSERT(CallTempReg2 != JSReturnReg_Type);
|
||||
MOZ_ASSERT(CallTempReg2 != JSReturnReg_Data);
|
||||
|
||||
LApplyArrayGeneric* lir = new(alloc()) LApplyArrayGeneric(
|
||||
useFixed(apply->getFunction(), CallTempReg3),
|
||||
useFixed(apply->getElements(), CallTempReg0),
|
||||
tempFixed(CallTempReg1), // object register
|
||||
tempFixed(CallTempReg2)); // stack counter register
|
||||
|
||||
MDefinition* self = apply->getThis();
|
||||
useBoxFixed(lir, LApplyArrayGeneric::ThisIndex, self, CallTempReg4, CallTempReg5);
|
||||
|
||||
// Bailout is needed in the case of possible non-JSFunction callee
|
||||
// too many values in the array, or empty space at the end of the
|
||||
// array. I'm going to use NonJSFunctionCallee for the code even
|
||||
// if that is not an adequate description.
|
||||
assignSnapshot(lir, Bailout_NonJSFunctionCallee);
|
||||
|
||||
defineReturn(lir, apply);
|
||||
assignSafepoint(lir, apply);
|
||||
}
|
||||
|
||||
void
|
||||
LIRGenerator::visitBail(MBail* bail)
|
||||
{
|
||||
|
|
|
@ -102,6 +102,7 @@ class LIRGenerator : public LIRGeneratorSpecific
|
|||
void visitComputeThis(MComputeThis* ins);
|
||||
void visitCall(MCall* call);
|
||||
void visitApplyArgs(MApplyArgs* apply);
|
||||
void visitApplyArray(MApplyArray* apply);
|
||||
void visitArraySplice(MArraySplice* splice);
|
||||
void visitBail(MBail* bail);
|
||||
void visitUnreachable(MUnreachable* unreachable);
|
||||
|
|
|
@ -1436,6 +1436,13 @@ MApplyArgs::New(TempAllocator& alloc, JSFunction* target, MDefinition* fun, MDef
|
|||
return new(alloc) MApplyArgs(target, fun, argc, self);
|
||||
}
|
||||
|
||||
MApplyArray*
|
||||
MApplyArray::New(TempAllocator& alloc, JSFunction* target, MDefinition* fun, MDefinition* elements,
|
||||
MDefinition* self)
|
||||
{
|
||||
return new(alloc) MApplyArray(target, fun, elements, self);
|
||||
}
|
||||
|
||||
MDefinition*
|
||||
MStringLength::foldsTo(TempAllocator& alloc)
|
||||
{
|
||||
|
|
|
@ -3906,6 +3906,49 @@ class MApplyArgs
|
|||
}
|
||||
};
|
||||
|
||||
// fun.apply(fn, array)
|
||||
class MApplyArray
|
||||
: public MAryInstruction<3>,
|
||||
public Mix3Policy<ObjectPolicy<0>, ObjectPolicy<1>, BoxPolicy<2> >::Data
|
||||
{
|
||||
protected:
|
||||
// Monomorphic cache of single target from TI, or nullptr.
|
||||
CompilerFunction target_;
|
||||
|
||||
MApplyArray(JSFunction* target, MDefinition* fun, MDefinition* elements, MDefinition* self)
|
||||
: target_(target)
|
||||
{
|
||||
initOperand(0, fun);
|
||||
initOperand(1, elements);
|
||||
initOperand(2, self);
|
||||
setResultType(MIRType_Value);
|
||||
}
|
||||
|
||||
public:
|
||||
INSTRUCTION_HEADER(ApplyArray)
|
||||
static MApplyArray* New(TempAllocator& alloc, JSFunction* target, MDefinition* fun,
|
||||
MDefinition* elements, MDefinition* self);
|
||||
|
||||
MDefinition* getFunction() const {
|
||||
return getOperand(0);
|
||||
}
|
||||
|
||||
// For TI-informed monomorphic callsites.
|
||||
JSFunction* getSingleTarget() const {
|
||||
return target_;
|
||||
}
|
||||
|
||||
MDefinition* getElements() const {
|
||||
return getOperand(1);
|
||||
}
|
||||
MDefinition* getThis() const {
|
||||
return getOperand(2);
|
||||
}
|
||||
bool possiblyCalls() const override {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class MBail : public MNullaryInstruction
|
||||
{
|
||||
protected:
|
||||
|
|
|
@ -64,6 +64,7 @@ namespace jit {
|
|||
_(ComputeThis) \
|
||||
_(Call) \
|
||||
_(ApplyArgs) \
|
||||
_(ApplyArray) \
|
||||
_(ArraySplice) \
|
||||
_(Bail) \
|
||||
_(Unreachable) \
|
||||
|
|
|
@ -1212,6 +1212,7 @@ FilterTypeSetPolicy::adjustInputs(TempAllocator& alloc, MInstruction* ins)
|
|||
_(Mix3Policy<ObjectPolicy<0>, IntPolicy<1>, BoxPolicy<2> >) \
|
||||
_(Mix3Policy<ObjectPolicy<0>, IntPolicy<1>, IntPolicy<2> >) \
|
||||
_(Mix3Policy<ObjectPolicy<0>, IntPolicy<1>, TruncateToInt32Policy<2> >) \
|
||||
_(Mix3Policy<ObjectPolicy<0>, ObjectPolicy<1>, BoxPolicy<2> >) \
|
||||
_(Mix3Policy<ObjectPolicy<0>, ObjectPolicy<1>, IntPolicy<2> >) \
|
||||
_(Mix3Policy<ObjectPolicy<0>, ObjectPolicy<1>, ObjectPolicy<2> >) \
|
||||
_(Mix3Policy<StringPolicy<0>, IntPolicy<1>, IntPolicy<2>>) \
|
||||
|
|
|
@ -1814,6 +1814,52 @@ class LApplyArgsGeneric : public LCallInstructionHelper<BOX_PIECES, BOX_PIECES +
|
|||
}
|
||||
};
|
||||
|
||||
class LApplyArrayGeneric : public LCallInstructionHelper<BOX_PIECES, BOX_PIECES + 2, 2>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(ApplyArrayGeneric)
|
||||
|
||||
LApplyArrayGeneric(const LAllocation& func, const LAllocation& elements,
|
||||
const LDefinition& tmpobjreg, const LDefinition& tmpcopy)
|
||||
{
|
||||
setOperand(0, func);
|
||||
setOperand(1, elements);
|
||||
setTemp(0, tmpobjreg);
|
||||
setTemp(1, tmpcopy);
|
||||
}
|
||||
|
||||
MApplyArray* mir() const {
|
||||
return mir_->toApplyArray();
|
||||
}
|
||||
|
||||
bool hasSingleTarget() const {
|
||||
return getSingleTarget() != nullptr;
|
||||
}
|
||||
JSFunction* getSingleTarget() const {
|
||||
return mir()->getSingleTarget();
|
||||
}
|
||||
|
||||
const LAllocation* getFunction() {
|
||||
return getOperand(0);
|
||||
}
|
||||
const LAllocation* getElements() {
|
||||
return getOperand(1);
|
||||
}
|
||||
// argc is mapped to the same register as elements: argc becomes
|
||||
// live as elements is dying, all registers are calltemps.
|
||||
const LAllocation* getArgc() {
|
||||
return getOperand(1);
|
||||
}
|
||||
static const size_t ThisIndex = 2;
|
||||
|
||||
const LDefinition* getTempObject() {
|
||||
return getTemp(0);
|
||||
}
|
||||
const LDefinition* getTempStackCounter() {
|
||||
return getTemp(1);
|
||||
}
|
||||
};
|
||||
|
||||
class LArraySplice : public LCallInstructionHelper<0, 3, 0>
|
||||
{
|
||||
public:
|
||||
|
|
|
@ -74,6 +74,7 @@
|
|||
_(CallGeneric) \
|
||||
_(CallNative) \
|
||||
_(ApplyArgsGeneric) \
|
||||
_(ApplyArrayGeneric) \
|
||||
_(Bail) \
|
||||
_(Unreachable) \
|
||||
_(EncodeSnapshot) \
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "mozilla/ArrayUtils.h"
|
||||
#include "mozilla/Casting.h"
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
|
||||
#include "jscntxt.h"
|
||||
#include "jscompartment.h"
|
||||
|
@ -53,6 +54,7 @@ using mozilla::IsInRange;
|
|||
using mozilla::Maybe;
|
||||
using mozilla::PodMove;
|
||||
using mozilla::UniquePtr;
|
||||
using mozilla::Maybe;
|
||||
|
||||
static void
|
||||
selfHosting_ErrorReporter(JSContext* cx, const char* message, JSErrorReport* report)
|
||||
|
|
Загрузка…
Ссылка в новой задаче