1344477 - Add dedicated opcode for function call ignoring return value.

This commit is contained in:
Fedor 2019-09-05 20:07:08 +03:00
parent 6083e3130a
commit 11748e030a
31 changed files with 271 additions and 241 deletions

View File

@ -128,7 +128,10 @@ class MOZ_STACK_CLASS CallArgsBase : public WantUsedRval
protected:
Value* argv_;
unsigned argc_;
bool constructing_;
bool constructing_:1;
// True if the caller does not use the return value.
bool ignoresReturnValue_:1;
public:
// CALLEE ACCESS
@ -164,6 +167,10 @@ class MOZ_STACK_CLASS CallArgsBase : public WantUsedRval
return true;
}
bool ignoresReturnValue() const {
return ignoresReturnValue_;
}
MutableHandleValue newTarget() const {
MOZ_ASSERT(constructing_);
return MutableHandleValue::fromMarkedLocation(&this->argv_[argc_]);
@ -280,14 +287,17 @@ class MOZ_STACK_CLASS CallArgs : public detail::CallArgsBase<detail::IncludeUsed
{
private:
friend CallArgs CallArgsFromVp(unsigned argc, Value* vp);
friend CallArgs CallArgsFromSp(unsigned stackSlots, Value* sp, bool constructing);
friend CallArgs CallArgsFromSp(unsigned stackSlots, Value* sp, bool constructing,
bool ignoresReturnValue);
static CallArgs create(unsigned argc, Value* argv, bool constructing) {
static CallArgs create(unsigned argc, Value* argv, bool constructing,
bool ignoresReturnValue = false) {
CallArgs args;
args.clearUsedRval();
args.argv_ = argv;
args.argc_ = argc;
args.constructing_ = constructing;
args.ignoresReturnValue_ = ignoresReturnValue;
#ifdef DEBUG
for (unsigned i = 0; i < argc; ++i)
MOZ_ASSERT_IF(argv[i].isGCThing(), !GCThingIsMarkedGray(GCCellPtr(argv[i])));
@ -314,9 +324,11 @@ CallArgsFromVp(unsigned argc, Value* vp)
// eventually move it to an internal header. Embedders should use
// JS::CallArgsFromVp!
MOZ_ALWAYS_INLINE CallArgs
CallArgsFromSp(unsigned stackSlots, Value* sp, bool constructing = false)
CallArgsFromSp(unsigned stackSlots, Value* sp, bool constructing = false,
bool ignoresReturnValue = false)
{
return CallArgs::create(stackSlots - constructing, sp - stackSlots, constructing);
return CallArgs::create(stackSlots - constructing, sp - stackSlots, constructing,
ignoresReturnValue);
}
} // namespace JS

View File

@ -4603,7 +4603,7 @@ BytecodeEmitter::emitSwitch(ParseNode* pn)
// If the expression is a literal, suppress line number emission so
// that debugging works more naturally.
if (caseValue) {
if (!emitTree(caseValue,
if (!emitTree(caseValue, ValueUsage::WantValue,
caseValue->isLiteral() ? SUPPRESS_LINENOTE : EMIT_LINENOTE))
{
return false;
@ -6652,7 +6652,7 @@ BytecodeEmitter::emitLexicalScopeBody(ParseNode* body, EmitLineNumberNote emitLi
}
// Line notes were updated by emitLexicalScope.
return emitTree(body, emitLineNote);
return emitTree(body, ValueUsage::WantValue, emitLineNote);
}
// Using MOZ_NEVER_INLINE in here is a workaround for llvm.org/pr14047. See
@ -6754,9 +6754,9 @@ BytecodeEmitter::emitRequireObjectCoercible()
return false;
if (!emit2(JSOP_PICK, 2)) // VAL REQUIREOBJECTCOERCIBLE UNDEFINED VAL
return false;
if (!emitCall(JSOP_CALL, 1)) // VAL IGNORED
if (!emitCall(JSOP_CALL_IGNORES_RV, 1))// VAL IGNORED
return false;
checkTypeSet(JSOP_CALL);
checkTypeSet(JSOP_CALL_IGNORES_RV);
if (!emit1(JSOP_POP)) // VAL
return false;
@ -7276,12 +7276,14 @@ BytecodeEmitter::emitCStyleFor(ParseNode* pn, EmitterScope* headLexicalEmitterSc
// scope, but we still need to emit code for the initializers.)
if (!updateSourceCoordNotes(init->pn_pos.begin))
return false;
if (!emitTree(init))
return false;
if (!init->isForLoopDeclaration()) {
if (init->isForLoopDeclaration()) {
if (!emitTree(init))
return false;
} else {
// 'init' is an expression, not a declaration. emitTree left its
// value on the stack.
if (!emitTree(init, ValueUsage::IgnoreValue))
return false;
if (!emit1(JSOP_POP))
return false;
}
@ -7363,7 +7365,7 @@ BytecodeEmitter::emitCStyleFor(ParseNode* pn, EmitterScope* headLexicalEmitterSc
if (!updateSourceCoordNotes(update->pn_pos.begin))
return false;
if (!emitTree(update))
if (!emitTree(update, ValueUsage::IgnoreValue))
return false;
if (!emit1(JSOP_POP))
return false;
@ -8690,8 +8692,9 @@ BytecodeEmitter::emitStatement(ParseNode* pn)
if (useful) {
JSOp op = wantval ? JSOP_SETRVAL : JSOP_POP;
ValueUsage valueUsage = wantval ? ValueUsage::WantValue : ValueUsage::IgnoreValue;
MOZ_ASSERT_IF(pn2->isKind(PNK_ASSIGN), pn2->isOp(JSOP_NOP));
if (!emitTree(pn2))
if (!emitTree(pn2, valueUsage))
return false;
if (!emit1(op))
return false;
@ -9041,7 +9044,7 @@ BytecodeEmitter::emitOptimizeSpread(ParseNode* arg0, JumpList* jmp, bool* emitte
}
bool
BytecodeEmitter::emitCallOrNew(ParseNode* pn)
BytecodeEmitter::emitCallOrNew(ParseNode* pn, ValueUsage valueUsage /* = ValueUsage::WantValue */)
{
bool callop = pn->isKind(PNK_CALL) || pn->isKind(PNK_TAGGED_TEMPLATE);
/*
@ -9220,13 +9223,20 @@ BytecodeEmitter::emitCallOrNew(ParseNode* pn)
}
if (!spread) {
if (!emitCall(pn->getOp(), argc, pn))
return false;
if (pn->getOp() == JSOP_CALL && valueUsage == ValueUsage::IgnoreValue) {
if (!emitCall(JSOP_CALL_IGNORES_RV, argc, pn))
return false;
checkTypeSet(JSOP_CALL_IGNORES_RV);
} else {
if (!emitCall(pn->getOp(), argc, pn))
return false;
checkTypeSet(pn->getOp());
}
} else {
if (!emit1(pn->getOp()))
return false;
checkTypeSet(pn->getOp());
}
checkTypeSet(pn->getOp());
if (pn->isOp(JSOP_EVAL) ||
pn->isOp(JSOP_STRICTEVAL) ||
pn->isOp(JSOP_SPREADEVAL) ||
@ -9324,12 +9334,13 @@ BytecodeEmitter::emitLogical(ParseNode* pn)
}
bool
BytecodeEmitter::emitSequenceExpr(ParseNode* pn)
BytecodeEmitter::emitSequenceExpr(ParseNode* pn,
ValueUsage valueUsage /* = ValueUsage::WantValue */)
{
for (ParseNode* child = pn->pn_head; ; child = child->pn_next) {
if (!updateSourceCoordNotes(child->pn_pos.begin))
return false;
if (!emitTree(child))
if (!emitTree(child, child->pn_next ? ValueUsage::IgnoreValue : valueUsage))
return false;
if (!child->pn_next)
break;
@ -9392,7 +9403,8 @@ BytecodeEmitter::emitLabeledStatement(const LabeledStatement* pn)
}
bool
BytecodeEmitter::emitConditionalExpression(ConditionalExpression& conditional)
BytecodeEmitter::emitConditionalExpression(ConditionalExpression& conditional,
ValueUsage valueUsage /* = ValueUsage::WantValue */)
{
/* Emit the condition, then branch if false to the else part. */
if (!emitTree(&conditional.condition()))
@ -9402,13 +9414,13 @@ BytecodeEmitter::emitConditionalExpression(ConditionalExpression& conditional)
if (!ifThenElse.emitCond())
return false;
if (!emitTreeInBranch(&conditional.thenExpression()))
if (!emitTreeInBranch(&conditional.thenExpression(), valueUsage))
return false;
if (!ifThenElse.emitElse())
return false;
if (!emitTreeInBranch(&conditional.elseExpression()))
if (!emitTreeInBranch(&conditional.elseExpression(), valueUsage))
return false;
if (!ifThenElse.emitEnd())
@ -10288,7 +10300,8 @@ BytecodeEmitter::emitClass(ParseNode* pn)
}
bool
BytecodeEmitter::emitTree(ParseNode* pn, EmitLineNumberNote emitLineNote)
BytecodeEmitter::emitTree(ParseNode* pn, ValueUsage valueUsage /* = ValueUsage::WantValue */,
EmitLineNumberNote emitLineNote /* = EMIT_LINENOTE */)
{
JS_CHECK_RECURSION(cx, return false);
@ -10410,7 +10423,7 @@ BytecodeEmitter::emitTree(ParseNode* pn, EmitLineNumberNote emitLineNote)
break;
case PNK_COMMA:
if (!emitSequenceExpr(pn))
if (!emitSequenceExpr(pn, valueUsage))
return false;
break;
@ -10432,7 +10445,7 @@ BytecodeEmitter::emitTree(ParseNode* pn, EmitLineNumberNote emitLineNote)
break;
case PNK_CONDITIONAL:
if (!emitConditionalExpression(pn->as<ConditionalExpression>()))
if (!emitConditionalExpression(pn->as<ConditionalExpression>(), valueUsage))
return false;
break;
@ -10545,7 +10558,7 @@ BytecodeEmitter::emitTree(ParseNode* pn, EmitLineNumberNote emitLineNote)
case PNK_CALL:
case PNK_GENEXP:
case PNK_SUPERCALL:
if (!emitCallOrNew(pn))
if (!emitCallOrNew(pn, valueUsage))
return false;
break;
@ -10703,12 +10716,13 @@ BytecodeEmitter::emitTree(ParseNode* pn, EmitLineNumberNote emitLineNote)
}
bool
BytecodeEmitter::emitTreeInBranch(ParseNode* pn)
BytecodeEmitter::emitTreeInBranch(ParseNode* pn,
ValueUsage valueUsage /* = ValueUsage::WantValue */)
{
// Code that may be conditionally executed always need their own TDZ
// cache.
TDZCheckCache tdzCache(this);
return emitTree(pn);
return emitTree(pn, valueUsage);
}
static bool

View File

@ -171,6 +171,11 @@ struct JumpList {
void patchAll(jsbytecode* code, JumpTarget target);
};
enum class ValueUsage {
WantValue,
IgnoreValue
};
struct MOZ_STACK_CLASS BytecodeEmitter
{
class TDZCheckCache;
@ -434,10 +439,12 @@ struct MOZ_STACK_CLASS BytecodeEmitter
};
// Emit code for the tree rooted at pn.
MOZ_MUST_USE bool emitTree(ParseNode* pn, EmitLineNumberNote emitLineNote = EMIT_LINENOTE);
MOZ_MUST_USE bool emitTree(ParseNode* pn, ValueUsage valueUsage = ValueUsage::WantValue,
EmitLineNumberNote emitLineNote = EMIT_LINENOTE);
// Emit code for the tree rooted at pn with its own TDZ cache.
MOZ_MUST_USE bool emitTreeInBranch(ParseNode* pn);
MOZ_MUST_USE bool emitTreeInBranch(ParseNode* pn,
ValueUsage valueUsage = ValueUsage::WantValue);
// Emit global, eval, or module code for tree rooted at body. Always
// encompasses the entire source.
@ -724,16 +731,18 @@ struct MOZ_STACK_CLASS BytecodeEmitter
MOZ_MUST_USE bool emitRightAssociative(ParseNode* pn);
MOZ_MUST_USE bool emitLeftAssociative(ParseNode* pn);
MOZ_MUST_USE bool emitLogical(ParseNode* pn);
MOZ_MUST_USE bool emitSequenceExpr(ParseNode* pn);
MOZ_MUST_USE bool emitSequenceExpr(ParseNode* pn,
ValueUsage valueUsage = ValueUsage::WantValue);
MOZ_NEVER_INLINE MOZ_MUST_USE bool emitIncOrDec(ParseNode* pn);
MOZ_MUST_USE bool emitConditionalExpression(ConditionalExpression& conditional);
MOZ_MUST_USE bool emitConditionalExpression(ConditionalExpression& conditional,
ValueUsage valueUsage = ValueUsage::WantValue);
MOZ_MUST_USE bool isRestParameter(ParseNode* pn, bool* result);
MOZ_MUST_USE bool emitOptimizeSpread(ParseNode* arg0, JumpList* jmp, bool* emitted);
MOZ_MUST_USE bool emitCallOrNew(ParseNode* pn);
MOZ_MUST_USE bool emitCallOrNew(ParseNode* pn, ValueUsage valueUsage = ValueUsage::WantValue);
MOZ_MUST_USE bool emitSelfHostedCallFunction(ParseNode* pn);
MOZ_MUST_USE bool emitSelfHostedResumeGenerator(ParseNode* pn);
MOZ_MUST_USE bool emitSelfHostedForceInterpreter(ParseNode* pn);

View File

@ -3291,6 +3291,12 @@ BaselineCompiler::emit_JSOP_CALL()
return emitCall();
}
bool
BaselineCompiler::emit_JSOP_CALL_IGNORES_RV()
{
return emitCall();
}
bool
BaselineCompiler::emit_JSOP_CALLITER()
{

View File

@ -159,6 +159,7 @@ namespace jit {
_(JSOP_INITALIASEDLEXICAL) \
_(JSOP_UNINITIALIZED) \
_(JSOP_CALL) \
_(JSOP_CALL_IGNORES_RV) \
_(JSOP_CALLITER) \
_(JSOP_FUNCALL) \
_(JSOP_FUNAPPLY) \

View File

@ -10,6 +10,8 @@
#include "mozilla/SizePrintfMacros.h"
#include "mozilla/TemplateLib.h"
#include "jsfriendapi.h"
#include "jsfun.h"
#include "jslibmath.h"
#include "jstypes.h"
@ -5494,11 +5496,17 @@ TryAttachCallStub(JSContext* cx, ICCall_Fallback* stub, HandleScript script, jsb
MOZ_ASSERT_IF(templateObject, !templateObject->group()->maybePreliminaryObjects());
}
bool ignoresReturnValue = false;
if (op == JSOP_CALL_IGNORES_RV && fun->isNative()) {
const JSJitInfo* jitInfo = fun->jitInfo();
ignoresReturnValue = jitInfo && jitInfo->type() == JSJitInfo::IgnoresReturnValueNative;
}
JitSpew(JitSpew_BaselineIC, " Generating Call_Native stub (fun=%p, cons=%s, spread=%s)",
fun.get(), constructing ? "yes" : "no", isSpread ? "yes" : "no");
ICCall_Native::Compiler compiler(cx, stub->fallbackMonitorStub()->firstMonitorStub(),
fun, templateObject, constructing, isSpread,
script->pcToOffset(pc));
fun, templateObject, constructing, ignoresReturnValue,
isSpread, script->pcToOffset(pc));
ICStub* newStub = compiler.getStub(compiler.getStubSpace(script));
if (!newStub)
return false;
@ -5601,12 +5609,14 @@ DoCallFallback(JSContext* cx, BaselineFrame* frame, ICCall_Fallback* stub_, uint
MOZ_ASSERT(argc == GET_ARGC(pc));
bool constructing = (op == JSOP_NEW);
bool ignoresReturnValue = (op == JSOP_CALL_IGNORES_RV);
// Ensure vp array is rooted - we may GC in here.
size_t numValues = argc + 2 + constructing;
AutoArrayRooter vpRoot(cx, numValues, vp);
CallArgs callArgs = CallArgsFromSp(argc + constructing, vp + numValues, constructing);
CallArgs callArgs = CallArgsFromSp(argc + constructing, vp + numValues, constructing,
ignoresReturnValue);
RootedValue callee(cx, vp[0]);
// Handle funapply with JSOP_ARGUMENTS
@ -5636,6 +5646,7 @@ DoCallFallback(JSContext* cx, BaselineFrame* frame, ICCall_Fallback* stub_, uint
return false;
} else {
MOZ_ASSERT(op == JSOP_CALL ||
op == JSOP_CALL_IGNORES_RV ||
op == JSOP_CALLITER ||
op == JSOP_FUNCALL ||
op == JSOP_FUNAPPLY ||
@ -6686,7 +6697,12 @@ ICCall_Native::Compiler::generateStubCode(MacroAssembler& masm)
// stub and use that instead of the original one.
masm.callWithABI(Address(ICStubReg, ICCall_Native::offsetOfNative()));
#else
masm.callWithABI(Address(callee, JSFunction::offsetOfNativeOrScript()));
if (ignoresReturnValue_) {
masm.loadPtr(Address(callee, JSFunction::offsetOfJitInfo()), callee);
masm.callWithABI(Address(callee, JSJitInfo::offsetOfIgnoresReturnValueNative()));
} else {
masm.callWithABI(Address(callee, JSFunction::offsetOfNativeOrScript()));
}
#endif
// Test for failure.

View File

@ -2277,6 +2277,7 @@ class ICSetProp_CallNative : public ICSetPropCallSetter
// Call
// JSOP_CALL
// JSOP_CALL_IGNORES_RV
// JSOP_FUNAPPLY
// JSOP_FUNCALL
// JSOP_NEW
@ -2547,6 +2548,7 @@ class ICCall_Native : public ICMonitoredStub
protected:
ICStub* firstMonitorStub_;
bool isConstructing_;
bool ignoresReturnValue_;
bool isSpread_;
RootedFunction callee_;
RootedObject templateObject_;
@ -2556,17 +2558,19 @@ class ICCall_Native : public ICMonitoredStub
virtual int32_t getKey() const {
return static_cast<int32_t>(engine_) |
(static_cast<int32_t>(kind) << 1) |
(static_cast<int32_t>(isConstructing_) << 17) |
(static_cast<int32_t>(isSpread_) << 18);
(static_cast<int32_t>(isSpread_) << 17) |
(static_cast<int32_t>(isConstructing_) << 18) |
(static_cast<int32_t>(ignoresReturnValue_) << 19);
}
public:
Compiler(JSContext* cx, ICStub* firstMonitorStub,
HandleFunction callee, HandleObject templateObject,
bool isConstructing, bool isSpread, uint32_t pcOffset)
bool isConstructing, bool ignoresReturnValue, bool isSpread, uint32_t pcOffset)
: ICCallStubCompiler(cx, ICStub::Call_Native),
firstMonitorStub_(firstMonitorStub),
isConstructing_(isConstructing),
ignoresReturnValue_(ignoresReturnValue),
isSpread_(isSpread),
callee_(cx, callee),
templateObject_(cx, templateObject),

View File

@ -3688,7 +3688,13 @@ CodeGenerator::visitCallNative(LCallNative* call)
masm.passABIArg(argContextReg);
masm.passABIArg(argUintNReg);
masm.passABIArg(argVpReg);
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, target->native()));
JSNative native = target->native();
if (call->ignoresReturnValue()) {
const JSJitInfo* jitInfo = target->jitInfo();
if (jitInfo && jitInfo->type() == JSJitInfo::IgnoresReturnValueNative)
native = jitInfo->ignoresReturnValueMethod;
}
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, native));
emitTracelogStopEvent(TraceLogger_Call);
@ -3845,13 +3851,15 @@ CodeGenerator::visitCallGetIntrinsicValue(LCallGetIntrinsicValue* lir)
callVM(GetIntrinsicValueInfo, lir);
}
typedef bool (*InvokeFunctionFn)(JSContext*, HandleObject, bool, uint32_t, Value*, MutableHandleValue);
typedef bool (*InvokeFunctionFn)(JSContext*, HandleObject, bool, bool, uint32_t, Value*,
MutableHandleValue);
static const VMFunction InvokeFunctionInfo =
FunctionInfo<InvokeFunctionFn>(InvokeFunction, "InvokeFunction");
void
CodeGenerator::emitCallInvokeFunction(LInstruction* call, Register calleereg,
bool constructing, uint32_t argc, uint32_t unusedStack)
bool constructing, bool ignoresReturnValue,
uint32_t argc, uint32_t unusedStack)
{
// Nestle %esp up to the argument vector.
// Each path must account for framePushed_ separately, for callVM to be valid.
@ -3859,6 +3867,7 @@ CodeGenerator::emitCallInvokeFunction(LInstruction* call, Register calleereg,
pushArg(masm.getStackPointer()); // argv.
pushArg(Imm32(argc)); // argc.
pushArg(Imm32(ignoresReturnValue));
pushArg(Imm32(constructing)); // constructing.
pushArg(calleereg); // JSFunction*.
@ -3945,8 +3954,8 @@ CodeGenerator::visitCallGeneric(LCallGeneric* call)
// Handle uncompiled or native functions.
masm.bind(&invoke);
emitCallInvokeFunction(call, calleereg, call->isConstructing(), call->numActualArgs(),
unusedStack);
emitCallInvokeFunction(call, calleereg, call->isConstructing(), call->ignoresReturnValue(),
call->numActualArgs(), unusedStack);
masm.bind(&end);
@ -4001,7 +4010,8 @@ CodeGenerator::visitCallKnown(LCallKnown* call)
masm.checkStackAlignment();
if (target->isClassConstructor() && !call->isConstructing()) {
emitCallInvokeFunction(call, calleereg, call->isConstructing(), call->numActualArgs(), unusedStack);
emitCallInvokeFunction(call, calleereg, call->isConstructing(), call->ignoresReturnValue(),
call->numActualArgs(), unusedStack);
return;
}
@ -4045,7 +4055,8 @@ CodeGenerator::visitCallKnown(LCallKnown* call)
if (call->isConstructing() && target->nargs() > call->numActualArgs())
emitCallInvokeFunctionShuffleNewTarget(call, calleereg, target->nargs(), unusedStack);
else
emitCallInvokeFunction(call, calleereg, call->isConstructing(), call->numActualArgs(), unusedStack);
emitCallInvokeFunction(call, calleereg, call->isConstructing(), call->ignoresReturnValue(),
call->numActualArgs(), unusedStack);
masm.bind(&end);
@ -4072,6 +4083,7 @@ CodeGenerator::emitCallInvokeFunction(T* apply, Register extraStackSize)
pushArg(objreg); // argv.
pushArg(ToRegister(apply->getArgc())); // argc.
pushArg(Imm32(false)); // ignoresReturnValue.
pushArg(Imm32(false)); // isConstrucing.
pushArg(ToRegister(apply->getFunction())); // JSFunction*.
@ -4428,19 +4440,6 @@ CodeGenerator::visitApplyArrayGeneric(LApplyArrayGeneric* apply)
emitApplyGeneric(apply);
}
typedef bool (*ArraySpliceDenseFn)(JSContext*, HandleObject, uint32_t, uint32_t);
static const VMFunction ArraySpliceDenseInfo =
FunctionInfo<ArraySpliceDenseFn>(ArraySpliceDense, "ArraySpliceDense");
void
CodeGenerator::visitArraySplice(LArraySplice* lir)
{
pushArg(ToRegister(lir->getDeleteCount()));
pushArg(ToRegister(lir->getStart()));
pushArg(ToRegister(lir->getObject()));
callVM(ArraySpliceDenseInfo, lir);
}
void
CodeGenerator::visitBail(LBail* lir)
{

View File

@ -165,8 +165,8 @@ class CodeGenerator final : public CodeGeneratorSpecific
void visitOutOfLineCallPostWriteElementBarrier(OutOfLineCallPostWriteElementBarrier* ool);
void visitCallNative(LCallNative* call);
void emitCallInvokeFunction(LInstruction* call, Register callereg,
bool isConstructing, uint32_t argc,
uint32_t unusedStack);
bool isConstructing, bool ignoresReturnValue,
uint32_t argc, uint32_t unusedStack);
void visitCallGeneric(LCallGeneric* call);
void emitCallInvokeFunctionShuffleNewTarget(LCallKnown *call,
Register calleeReg,
@ -251,7 +251,6 @@ class CodeGenerator final : public CodeGeneratorSpecific
void emitSetPropertyPolymorphic(LInstruction* lir, Register obj,
Register scratch, const ConstantOrRegister& value);
void visitSetPropertyPolymorphicV(LSetPropertyPolymorphicV* ins);
void visitArraySplice(LArraySplice* splice);
void visitSetPropertyPolymorphicT(LSetPropertyPolymorphicT* ins);
void visitAbsI(LAbsI* lir);
void visitAtan2D(LAtan2D* lir);

View File

@ -15,7 +15,6 @@
_(ArrayShift) \
_(ArrayPush) \
_(ArraySlice) \
_(ArraySplice) \
\
_(AtomicsCompareExchange) \
_(AtomicsExchange) \

View File

@ -1939,6 +1939,7 @@ IonBuilder::inspectOpcode(JSOp op)
return jsop_funapply(GET_ARGC(pc));
case JSOP_CALL:
case JSOP_CALL_IGNORES_RV:
case JSOP_CALLITER:
case JSOP_NEW:
case JSOP_SUPERCALL:
@ -1946,7 +1947,8 @@ IonBuilder::inspectOpcode(JSOp op)
if (!outermostBuilder()->iterators_.append(current->peek(-1)))
return false;
}
return jsop_call(GET_ARGC(pc), (JSOp)*pc == JSOP_NEW || (JSOp)*pc == JSOP_SUPERCALL);
return jsop_call(GET_ARGC(pc), (JSOp)*pc == JSOP_NEW || (JSOp)*pc == JSOP_SUPERCALL,
(JSOp)*pc == JSOP_CALL_IGNORES_RV);
case JSOP_EVAL:
case JSOP_STRICTEVAL:
@ -5874,7 +5876,7 @@ IonBuilder::inlineGenericFallback(JSFunction* target, CallInfo& callInfo, MBasic
return false;
// Create a new CallInfo to track modified state within this block.
CallInfo fallbackInfo(alloc(), callInfo.constructing());
CallInfo fallbackInfo(alloc(), callInfo.constructing(), callInfo.ignoresReturnValue());
if (!fallbackInfo.init(callInfo))
return false;
fallbackInfo.popFormals(fallbackBlock);
@ -5913,7 +5915,7 @@ IonBuilder::inlineObjectGroupFallback(CallInfo& callInfo, MBasicBlock* dispatchB
MOZ_ASSERT(cache->idempotent());
// Create a new CallInfo to track modified state within the fallback path.
CallInfo fallbackInfo(alloc(), callInfo.constructing());
CallInfo fallbackInfo(alloc(), callInfo.constructing(), callInfo.ignoresReturnValue());
if (!fallbackInfo.init(callInfo))
return false;
@ -6089,7 +6091,7 @@ IonBuilder::inlineCalls(CallInfo& callInfo, const ObjectVector& targets, BoolVec
inlineBlock->rewriteSlot(funIndex, funcDef);
// Create a new CallInfo to track modified state within the inline block.
CallInfo inlineInfo(alloc(), callInfo.constructing());
CallInfo inlineInfo(alloc(), callInfo.constructing(), callInfo.ignoresReturnValue());
if (!inlineInfo.init(callInfo))
return false;
inlineInfo.popFormals(inlineBlock);
@ -6538,7 +6540,8 @@ IonBuilder::jsop_funcall(uint32_t argc)
TemporaryTypeSet* calleeTypes = current->peek(calleeDepth)->resultTypeSet();
JSFunction* native = getSingleCallTarget(calleeTypes);
if (!native || !native->isNative() || native->native() != &fun_call) {
CallInfo callInfo(alloc(), false);
CallInfo callInfo(alloc(), /* constructing = */ false,
/* ignoresReturnValue = */ BytecodeIsPopped(pc));
if (!callInfo.init(current, argc))
return false;
return makeCall(native, callInfo);
@ -6563,7 +6566,8 @@ IonBuilder::jsop_funcall(uint32_t argc)
argc -= 1;
}
CallInfo callInfo(alloc(), false);
CallInfo callInfo(alloc(), /* constructing = */ false,
/* ignoresReturnValue = */ BytecodeIsPopped(pc));
if (!callInfo.init(current, argc))
return false;
@ -6600,7 +6604,8 @@ IonBuilder::jsop_funapply(uint32_t argc)
TemporaryTypeSet* calleeTypes = current->peek(calleeDepth)->resultTypeSet();
JSFunction* native = getSingleCallTarget(calleeTypes);
if (argc != 2 || info().analysisMode() == Analysis_ArgumentsUsage) {
CallInfo callInfo(alloc(), false);
CallInfo callInfo(alloc(), /* constructing = */ false,
/* ignoresReturnValue = */ BytecodeIsPopped(pc));
if (!callInfo.init(current, argc))
return false;
return makeCall(native, callInfo);
@ -6629,7 +6634,8 @@ IonBuilder::jsop_funapply(uint32_t argc)
return jsop_funapplyarray(argc);
}
CallInfo callInfo(alloc(), false);
CallInfo callInfo(alloc(), /* constructing = */ false,
/* ignoresReturnValue = */ BytecodeIsPopped(pc));
if (!callInfo.init(current, argc))
return false;
return makeCall(native, callInfo);
@ -6738,7 +6744,8 @@ IonBuilder::jsop_funapplyarguments(uint32_t argc)
// can inline the apply() target and don't care about the actual arguments
// that were passed in.
CallInfo callInfo(alloc(), false);
CallInfo callInfo(alloc(), /* constructing = */ false,
/* ignoresReturnValue = */ BytecodeIsPopped(pc));
// Vp
MDefinition* vp = current->pop();
@ -6784,7 +6791,7 @@ IonBuilder::jsop_funapplyarguments(uint32_t argc)
}
bool
IonBuilder::jsop_call(uint32_t argc, bool constructing)
IonBuilder::jsop_call(uint32_t argc, bool constructing, bool ignoresReturnValue)
{
startTrackingOptimizations();
@ -6810,7 +6817,7 @@ IonBuilder::jsop_call(uint32_t argc, bool constructing)
if (calleeTypes && !getPolyCallTargets(calleeTypes, constructing, targets, 4))
return false;
CallInfo callInfo(alloc(), constructing);
CallInfo callInfo(alloc(), constructing, ignoresReturnValue);
if (!callInfo.init(current, argc))
return false;
@ -6946,7 +6953,8 @@ IonBuilder::makeCallHelper(JSFunction* target, CallInfo& callInfo)
}
MCall* call = MCall::New(alloc(), target, targetArgs + 1 + callInfo.constructing(),
callInfo.argc(), callInfo.constructing(), isDOMCall);
callInfo.argc(), callInfo.constructing(),
callInfo.ignoresReturnValue(), isDOMCall);
if (!call)
return nullptr;
@ -7047,7 +7055,7 @@ IonBuilder::jsop_eval(uint32_t argc)
// Emit a normal call if the eval has never executed. This keeps us from
// disabling compilation for the script when testing with --ion-eager.
if (calleeTypes && calleeTypes->empty())
return jsop_call(argc, /* constructing = */ false);
return jsop_call(argc, /* constructing = */ false, false);
JSFunction* singleton = getSingleCallTarget(calleeTypes);
if (!singleton)
@ -7063,7 +7071,8 @@ IonBuilder::jsop_eval(uint32_t argc)
if (info().funMaybeLazy()->isArrow())
return abort("Direct eval from arrow function");
CallInfo callInfo(alloc(), /* constructing = */ false);
CallInfo callInfo(alloc(), /* constructing = */ false,
/* ignoresReturnValue = */ BytecodeIsPopped(pc));
if (!callInfo.init(current, argc))
return false;
callInfo.setImplicitlyUsedUnchecked();
@ -7102,7 +7111,8 @@ IonBuilder::jsop_eval(uint32_t argc)
current->push(dynamicName);
current->push(constant(UndefinedValue())); // thisv
CallInfo evalCallInfo(alloc(), /* constructing = */ false);
CallInfo evalCallInfo(alloc(), /* constructing = */ false,
/* ignoresReturnValue = */ BytecodeIsPopped(pc));
if (!evalCallInfo.init(current, /* argc = */ 0))
return false;
@ -7119,7 +7129,7 @@ IonBuilder::jsop_eval(uint32_t argc)
return resumeAfter(ins) && pushTypeBarrier(ins, types, BarrierKind::TypeSet);
}
return jsop_call(argc, /* constructing = */ false);
return jsop_call(argc, /* constructing = */ false, false);
}
bool
@ -12001,7 +12011,8 @@ IonBuilder::getPropTryCommonGetter(bool* emitted, MDefinition* obj, PropertyName
current->push(obj);
CallInfo callInfo(alloc(), false);
CallInfo callInfo(alloc(), /* constructing = */ false,
/* ignoresReturnValue = */ BytecodeIsPopped(pc));
if (!callInfo.init(current, 0))
return false;
@ -12496,7 +12507,8 @@ IonBuilder::setPropTryCommonSetter(bool* emitted, MDefinition* obj,
// Call the setter. Note that we have to push the original value, not
// the setter's return value.
CallInfo callInfo(alloc(), false);
CallInfo callInfo(alloc(), /* constructing = */ false,
/* ignoresReturnValue = */ BytecodeIsPopped(pc));
if (!callInfo.init(current, 1))
return false;

View File

@ -699,6 +699,7 @@ class IonBuilder
MOZ_MUST_USE bool jsop_funapplyarguments(uint32_t argc);
MOZ_MUST_USE bool jsop_funapplyarray(uint32_t argc);
MOZ_MUST_USE bool jsop_call(uint32_t argc, bool constructing);
MOZ_MUST_USE bool jsop_call(uint32_t argc, bool constructing, bool ignoresReturnValue);
MOZ_MUST_USE bool jsop_eval(uint32_t argc);
MOZ_MUST_USE bool jsop_ifeq(JSOp op);
MOZ_MUST_USE bool jsop_try();
@ -820,7 +821,6 @@ class IonBuilder
InliningStatus inlineArrayPush(CallInfo& callInfo);
InliningStatus inlineArraySlice(CallInfo& callInfo);
InliningStatus inlineArrayJoin(CallInfo& callInfo);
InliningStatus inlineArraySplice(CallInfo& callInfo);
// Math natives.
InliningStatus inlineMathAbs(CallInfo& callInfo);
@ -1352,16 +1352,21 @@ class CallInfo
MDefinition* newTargetArg_;
MDefinitionVector args_;
bool constructing_;
bool setter_;
bool constructing_:1;
// True if the caller does not use the return value.
bool ignoresReturnValue_:1;
bool setter_:1;
public:
CallInfo(TempAllocator& alloc, bool constructing)
CallInfo(TempAllocator& alloc, bool constructing, bool ignoresReturnValue)
: fun_(nullptr),
thisArg_(nullptr),
newTargetArg_(nullptr),
args_(alloc),
constructing_(constructing),
ignoresReturnValue_(ignoresReturnValue),
setter_(false)
{ }
@ -1370,6 +1375,7 @@ class CallInfo
fun_ = callInfo.fun();
thisArg_ = callInfo.thisArg();
ignoresReturnValue_ = callInfo.ignoresReturnValue();
if (constructing())
newTargetArg_ = callInfo.getNewTarget();
@ -1466,6 +1472,10 @@ class CallInfo
return constructing_;
}
bool ignoresReturnValue() const {
return ignoresReturnValue_;
}
void setNewTarget(MDefinition* newTarget) {
MOZ_ASSERT(constructing());
newTargetArg_ = newTarget;

View File

@ -656,16 +656,6 @@ LIRGenerator::visitAssertRecoveredOnBailout(MAssertRecoveredOnBailout* assertion
MOZ_CRASH("AssertRecoveredOnBailout nodes are always recovered on bailouts.");
}
void
LIRGenerator::visitArraySplice(MArraySplice* ins)
{
LArraySplice* lir = new(alloc()) LArraySplice(useRegisterAtStart(ins->object()),
useRegisterAtStart(ins->start()),
useRegisterAtStart(ins->deleteCount()));
add(lir, ins);
assignSafepoint(lir, ins);
}
void
LIRGenerator::visitGetDynamicName(MGetDynamicName* ins)
{

View File

@ -107,7 +107,6 @@ class LIRGenerator : public LIRGeneratorSpecific
void visitCall(MCall* call);
void visitApplyArgs(MApplyArgs* apply);
void visitApplyArray(MApplyArray* apply);
void visitArraySplice(MArraySplice* splice);
void visitBail(MBail* bail);
void visitUnreachable(MUnreachable* unreachable);
void visitEncodeSnapshot(MEncodeSnapshot* ins);

View File

@ -85,8 +85,6 @@ IonBuilder::inlineNativeCall(CallInfo& callInfo, JSFunction* target)
return inlineArrayPush(callInfo);
case InlinableNative::ArraySlice:
return inlineArraySlice(callInfo);
case InlinableNative::ArraySplice:
return inlineArraySplice(callInfo);
// Atomic natives.
case InlinableNative::AtomicsCompareExchange:
@ -653,46 +651,6 @@ IonBuilder::inlineArrayPopShift(CallInfo& callInfo, MArrayPopShift::Mode mode)
return InliningStatus_Inlined;
}
IonBuilder::InliningStatus
IonBuilder::inlineArraySplice(CallInfo& callInfo)
{
if (callInfo.argc() != 2 || callInfo.constructing()) {
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
return InliningStatus_NotInlined;
}
// Ensure |this|, argument and result are objects.
if (getInlineReturnType() != MIRType::Object)
return InliningStatus_NotInlined;
if (callInfo.thisArg()->type() != MIRType::Object)
return InliningStatus_NotInlined;
if (callInfo.getArg(0)->type() != MIRType::Int32)
return InliningStatus_NotInlined;
if (callInfo.getArg(1)->type() != MIRType::Int32)
return InliningStatus_NotInlined;
callInfo.setImplicitlyUsedUnchecked();
// Specialize arr.splice(start, deleteCount) with unused return value and
// avoid creating the result array in this case.
if (!BytecodeIsPopped(pc)) {
trackOptimizationOutcome(TrackedOutcome::CantInlineGeneric);
return InliningStatus_NotInlined;
}
MArraySplice* ins = MArraySplice::New(alloc(),
callInfo.thisArg(),
callInfo.getArg(0),
callInfo.getArg(1));
current->add(ins);
pushConstant(UndefinedValue());
if (!resumeAfter(ins))
return InliningStatus_Error;
return InliningStatus_Inlined;
}
IonBuilder::InliningStatus
IonBuilder::inlineArrayJoin(CallInfo& callInfo)
{

View File

@ -1970,7 +1970,7 @@ WrappedFunction::WrappedFunction(JSFunction* fun)
MCall*
MCall::New(TempAllocator& alloc, JSFunction* target, size_t maxArgc, size_t numActualArgs,
bool construct, bool isDOMCall)
bool construct, bool ignoresReturnValue, bool isDOMCall)
{
WrappedFunction* wrappedTarget = target ? new(alloc) WrappedFunction(target) : nullptr;
MOZ_ASSERT(maxArgc >= numActualArgs);
@ -1979,7 +1979,7 @@ MCall::New(TempAllocator& alloc, JSFunction* target, size_t maxArgc, size_t numA
MOZ_ASSERT(!construct);
ins = new(alloc) MCallDOMNative(wrappedTarget, numActualArgs);
} else {
ins = new(alloc) MCall(wrappedTarget, numActualArgs, construct);
ins = new(alloc) MCall(wrappedTarget, numActualArgs, construct, ignoresReturnValue);
}
if (!ins->init(alloc, maxArgc + NumNonArgumentOperands))
return nullptr;

View File

@ -4043,14 +4043,18 @@ class MCall
uint32_t numActualArgs_;
// True if the call is for JSOP_NEW.
bool construct_;
bool construct_:1;
bool needsArgCheck_;
// True if the caller does not use the return value.
bool ignoresReturnValue_:1;
MCall(WrappedFunction* target, uint32_t numActualArgs, bool construct)
bool needsArgCheck_:1;
MCall(WrappedFunction* target, uint32_t numActualArgs, bool construct, bool ignoresReturnValue)
: target_(target),
numActualArgs_(numActualArgs),
construct_(construct),
ignoresReturnValue_(ignoresReturnValue),
needsArgCheck_(true)
{
setResultType(MIRType::Value);
@ -4059,7 +4063,7 @@ class MCall
public:
INSTRUCTION_HEADER(Call)
static MCall* New(TempAllocator& alloc, JSFunction* target, size_t maxArgc, size_t numActualArgs,
bool construct, bool isDOMCall);
bool construct, bool ignoresReturnValue, bool isDOMCall);
void initFunction(MDefinition* func) {
initOperand(FunctionOperandIndex, func);
@ -4104,6 +4108,10 @@ class MCall
return construct_;
}
bool ignoresReturnValue() const {
return ignoresReturnValue_;
}
// The number of stack arguments is the max between the number of formal
// arguments and the number of actual arguments. The number of stack
// argument includes the |undefined| padding added in case of underflow.
@ -4147,7 +4155,7 @@ class MCallDOMNative : public MCall
// virtual things from MCall.
protected:
MCallDOMNative(WrappedFunction* target, uint32_t numActualArgs)
: MCall(target, numActualArgs, false)
: MCall(target, numActualArgs, false, false)
{
MOZ_ASSERT(getJitInfo()->type() != JSJitInfo::InlinableNative);
@ -4162,7 +4170,8 @@ class MCallDOMNative : public MCall
}
friend MCall* MCall::New(TempAllocator& alloc, JSFunction* target, size_t maxArgc,
size_t numActualArgs, bool construct, bool isDOMCall);
size_t numActualArgs, bool construct, bool ignoresReturnValue,
bool isDOMCall);
const JSJitInfo* getJitInfo() const;
public:
@ -4177,27 +4186,6 @@ class MCallDOMNative : public MCall
virtual void computeMovable() override;
};
// arr.splice(start, deleteCount) with unused return value.
class MArraySplice
: public MTernaryInstruction,
public Mix3Policy<ObjectPolicy<0>, IntPolicy<1>, IntPolicy<2> >::Data
{
private:
MArraySplice(MDefinition* object, MDefinition* start, MDefinition* deleteCount)
: MTernaryInstruction(object, start, deleteCount)
{ }
public:
INSTRUCTION_HEADER(ArraySplice)
TRIVIAL_NEW_WRAPPERS
NAMED_OPERANDS((0, object), (1, start), (2, deleteCount))
bool possiblyCalls() const override {
return true;
}
};
// fun.apply(self, arguments)
class MApplyArgs
: public MAryInstruction<3>,

View File

@ -69,7 +69,6 @@ namespace jit {
_(Call) \
_(ApplyArgs) \
_(ApplyArray) \
_(ArraySplice) \
_(Bail) \
_(Unreachable) \
_(EncodeSnapshot) \

View File

@ -54,8 +54,8 @@ VMFunction::addToFunctions()
}
bool
InvokeFunction(JSContext* cx, HandleObject obj, bool constructing, uint32_t argc, Value* argv,
MutableHandleValue rval)
InvokeFunction(JSContext* cx, HandleObject obj, bool constructing, bool ignoresReturnValue,
uint32_t argc, Value* argv, MutableHandleValue rval)
{
TraceLoggerThread* logger = TraceLoggerForMainThread(cx->runtime());
TraceLogStartEvent(logger, TraceLogger_Call);
@ -104,7 +104,7 @@ InvokeFunction(JSContext* cx, HandleObject obj, bool constructing, uint32_t argc
return InternalConstructWithProvidedThis(cx, fval, thisv, cargs, newTarget, rval);
}
InvokeArgs args(cx);
InvokeArgsMaybeIgnoresReturnValue args(cx, ignoresReturnValue);
if (!args.init(cx, argc))
return false;
@ -120,7 +120,7 @@ InvokeFunctionShuffleNewTarget(JSContext* cx, HandleObject obj, uint32_t numActu
{
MOZ_ASSERT(numFormalArgs > numActualArgs);
argv[1 + numActualArgs] = argv[1 + numFormalArgs];
return InvokeFunction(cx, obj, true, numActualArgs, argv, rval);
return InvokeFunction(cx, obj, true, false, numActualArgs, argv, rval);
}
bool
@ -303,18 +303,6 @@ StringsEqual(JSContext* cx, HandleString lhs, HandleString rhs, bool* res)
template bool StringsEqual<true>(JSContext* cx, HandleString lhs, HandleString rhs, bool* res);
template bool StringsEqual<false>(JSContext* cx, HandleString lhs, HandleString rhs, bool* res);
bool
ArraySpliceDense(JSContext* cx, HandleObject obj, uint32_t start, uint32_t deleteCount)
{
JS::AutoValueArray<4> argv(cx);
argv[0].setUndefined();
argv[1].setObject(*obj);
argv[2].set(Int32Value(start));
argv[3].set(Int32Value(deleteCount));
return js::array_splice_impl(cx, 2, argv.begin(), false);
}
bool
ArrayPopDense(JSContext* cx, HandleObject obj, MutableHandleValue rval)
{

View File

@ -584,8 +584,8 @@ class AutoDetectInvalidation
};
MOZ_MUST_USE bool
InvokeFunction(JSContext* cx, HandleObject obj0, bool constructing, uint32_t argc, Value* argv,
MutableHandleValue rval);
InvokeFunction(JSContext* cx, HandleObject obj0, bool constructing, bool ignoresReturnValue,
uint32_t argc, Value* argv, MutableHandleValue rval);
MOZ_MUST_USE bool
InvokeFunctionShuffleNewTarget(JSContext* cx, HandleObject obj, uint32_t numActualArgs,
uint32_t numFormalArgs, Value* argv, MutableHandleValue rval);
@ -738,9 +738,6 @@ InitBaselineFrameForOsr(BaselineFrame* frame, InterpreterFrame* interpFrame,
JSObject* CreateDerivedTypedObj(JSContext* cx, HandleObject descr,
HandleObject owner, int32_t offset);
MOZ_MUST_USE bool
ArraySpliceDense(JSContext* cx, HandleObject obj, uint32_t start, uint32_t deleteCount);
MOZ_MUST_USE bool
Recompile(JSContext* cx);
MOZ_MUST_USE bool

View File

@ -1898,6 +1898,9 @@ class LJSCallInstructionHelper : public LCallInstructionHelper<Defs, Operands, T
bool isConstructing() const {
return mir()->isConstructing();
}
bool ignoresReturnValue() const {
return mir()->ignoresReturnValue();
}
};
// Generates a polymorphic callsite, wherein the function being called is
@ -2218,34 +2221,6 @@ class LApplyArrayGeneric : public LCallInstructionHelper<BOX_PIECES, BOX_PIECES
}
};
class LArraySplice : public LCallInstructionHelper<0, 3, 0>
{
public:
LIR_HEADER(ArraySplice)
LArraySplice(const LAllocation& object, const LAllocation& start,
const LAllocation& deleteCount)
{
setOperand(0, object);
setOperand(1, start);
setOperand(2, deleteCount);
}
MArraySplice* mir() const {
return mir_->toArraySplice();
}
const LAllocation* getObject() {
return getOperand(0);
}
const LAllocation* getStart() {
return getOperand(1);
}
const LAllocation* getDeleteCount() {
return getOperand(2);
}
};
class LGetDynamicName : public LCallInstructionHelper<BOX_PIECES, 2, 3>
{
public:

View File

@ -69,7 +69,6 @@
_(NewArrayDynamicLength) \
_(NewTypedArray) \
_(NewTypedArrayDynamicLength) \
_(ArraySplice) \
_(NewObject) \
_(NewTypedObject) \
_(NewNamedLambdaObject) \

View File

@ -2380,13 +2380,6 @@ CopyDenseElements(JSContext* cx, NativeObject* dst, NativeObject* src,
return DenseElementResult::Success;
}
/* ES 2016 draft Mar 25, 2016 22.1.3.26. */
bool
js::array_splice(JSContext* cx, unsigned argc, Value* vp)
{
return array_splice_impl(cx, argc, vp, true);
}
static inline bool
ArraySpliceCopy(JSContext* cx, HandleObject arr, HandleObject obj,
uint32_t actualStart, uint32_t actualDeleteCount)
@ -2416,8 +2409,8 @@ ArraySpliceCopy(JSContext* cx, HandleObject arr, HandleObject obj,
return SetLengthProperty(cx, arr, actualDeleteCount);
}
bool
js::array_splice_impl(JSContext* cx, unsigned argc, Value* vp, bool returnValueIsUsed)
static bool
array_splice_impl(JSContext* cx, unsigned argc, Value* vp, bool returnValueIsUsed)
{
AutoSPSEntry pseudoFrame(cx->runtime(), "Array.prototype.splice");
CallArgs args = CallArgsFromVp(argc, vp);
@ -2667,6 +2660,19 @@ js::array_splice_impl(JSContext* cx, unsigned argc, Value* vp, bool returnValueI
return true;
}
/* ES 2016 draft Mar 25, 2016 22.1.3.26. */
bool
js::array_splice(JSContext* cx, unsigned argc, Value* vp)
{
return array_splice_impl(cx, argc, vp, true);
}
static bool
array_splice_noRetVal(JSContext* cx, unsigned argc, Value* vp)
{
return array_splice_impl(cx, argc, vp, false);
}
struct SortComparatorIndexes
{
bool operator()(uint32_t a, uint32_t b, bool* lessOrEqualp) {
@ -3084,6 +3090,15 @@ array_of(JSContext* cx, unsigned argc, Value* vp)
return true;
}
const JSJitInfo js::array_splice_info = {
{ (JSJitGetterOp)array_splice_noRetVal },
{ 0 }, /* unused */
{ 0 }, /* unused */
JSJitInfo::IgnoresReturnValueNative,
JSJitInfo::AliasEverything,
JSVAL_TYPE_UNDEFINED,
};
static const JSFunctionSpec array_methods[] = {
#if JS_HAS_TOSOURCE
JS_FN(js_toSource_str, array_toSource, 0,0),
@ -3099,7 +3114,7 @@ static const JSFunctionSpec array_methods[] = {
JS_INLINABLE_FN("pop", array_pop, 0,0, ArrayPop),
JS_INLINABLE_FN("shift", array_shift, 0,0, ArrayShift),
JS_FN("unshift", array_unshift, 1,0),
JS_INLINABLE_FN("splice", array_splice, 2,0, ArraySplice),
JS_FNINFO("splice", array_splice, &array_splice_info, 2,0),
/* Pythonic sequence methods. */
JS_SELF_HOSTED_FN("concat", "ArrayConcat", 1,0),

View File

@ -146,9 +146,6 @@ array_push(JSContext* cx, unsigned argc, js::Value* vp);
extern bool
array_pop(JSContext* cx, unsigned argc, js::Value* vp);
extern bool
array_splice_impl(JSContext* cx, unsigned argc, js::Value* vp, bool pop);
extern bool
array_join(JSContext* cx, unsigned argc, js::Value* vp);
@ -173,6 +170,8 @@ array_reverse(JSContext* cx, unsigned argc, js::Value* vp);
extern bool
array_splice(JSContext* cx, unsigned argc, js::Value* vp);
extern const JSJitInfo array_splice_info;
/*
* Append the given (non-hole) value to the end of an array. The array must be
* a newborn array -- that is, one which has not been exposed to script for

View File

@ -2254,6 +2254,7 @@ struct JSJitInfo {
Method,
StaticMethod,
InlinableNative,
IgnoresReturnValueNative,
// Must be last
OpTypeCount
};
@ -2345,8 +2346,13 @@ struct JSJitInfo {
JSJitMethodOp method;
/** A DOM static method, used for Promise wrappers */
JSNative staticMethod;
JSNative ignoresReturnValueMethod;
};
static unsigned offsetOfIgnoresReturnValueNative() {
return offsetof(JSJitInfo, ignoresReturnValueMethod);
}
union {
uint16_t protoID;
js::jit::InlinableNative inlinableNative;

View File

@ -585,6 +585,10 @@ class JSFunction : public js::NativeObject
return offsetof(JSFunction, u.nativeOrScript);
}
static unsigned offsetOfJitInfo() {
return offsetof(JSFunction, u.n.jitinfo);
}
inline void trace(JSTracer* trc);
/* Bound function accessors. */

View File

@ -132,7 +132,8 @@ js::StackUses(JSScript* script, jsbytecode* pc)
return 2 + GET_ARGC(pc) + 1;
default:
/* stack: fun, this, [argc arguments] */
MOZ_ASSERT(op == JSOP_CALL || op == JSOP_EVAL || op == JSOP_CALLITER ||
MOZ_ASSERT(op == JSOP_CALL || op == JSOP_CALL_IGNORES_RV || op == JSOP_EVAL ||
op == JSOP_CALLITER ||
op == JSOP_STRICTEVAL || op == JSOP_FUNCALL || op == JSOP_FUNAPPLY);
return 2 + GET_ARGC(pc);
}
@ -1363,6 +1364,7 @@ ExpressionDecompiler::decompilePC(jsbytecode* pc)
case JSOP_NEWTARGET:
return write("new.target");
case JSOP_CALL:
case JSOP_CALL_IGNORES_RV:
case JSOP_CALLITER:
case JSOP_FUNCALL:
return decompilePCForStackOperand(pc, -int32_t(GET_ARGC(pc) + 2)) &&
@ -1662,7 +1664,7 @@ DecompileArgumentFromStack(JSContext* cx, int formalIndex, char** res)
/* Don't handle getters, setters or calls from fun.call/fun.apply. */
JSOp op = JSOp(*current);
if (op != JSOP_CALL && op != JSOP_NEW)
if (op != JSOP_CALL && op != JSOP_CALL_IGNORES_RV && op != JSOP_NEW)
return true;
if (static_cast<unsigned>(formalIndex) >= GET_ARGC(current))
@ -1725,6 +1727,8 @@ js::CallResultEscapes(jsbytecode* pc)
if (*pc == JSOP_CALL)
pc += JSOP_CALL_LENGTH;
else if (*pc == JSOP_CALL_IGNORES_RV)
pc += JSOP_CALL_IGNORES_RV_LENGTH;
else if (*pc == JSOP_SPREADCALL)
pc += JSOP_SPREADCALL_LENGTH;
else

View File

@ -470,7 +470,13 @@ js::InternalCallOrConstruct(JSContext* cx, const CallArgs& args, MaybeConstruct
if (fun->isNative()) {
MOZ_ASSERT_IF(construct, !fun->isConstructor());
return CallJSNative(cx, fun->native(), args);
JSNative native = fun->native();
if (!construct && args.ignoresReturnValue()) {
const JSJitInfo* jitInfo = fun->jitInfo();
if (jitInfo && jitInfo->type() == JSJitInfo::IgnoresReturnValueNative)
native = jitInfo->ignoresReturnValueMethod;
}
return CallJSNative(cx, native, args);
}
if (!JSFunction::getOrCreateScript(cx, fun))
@ -2975,6 +2981,7 @@ CASE(JSOP_FUNAPPLY)
CASE(JSOP_NEW)
CASE(JSOP_CALL)
CASE(JSOP_CALL_IGNORES_RV)
CASE(JSOP_CALLITER)
CASE(JSOP_SUPERCALL)
CASE(JSOP_FUNCALL)
@ -2983,10 +2990,11 @@ CASE(JSOP_FUNCALL)
cx->runtime()->spsProfiler.updatePC(script, REGS.pc);
MaybeConstruct construct = MaybeConstruct(*REGS.pc == JSOP_NEW || *REGS.pc == JSOP_SUPERCALL);
bool ignoresReturnValue = *REGS.pc == JSOP_CALL_IGNORES_RV;
unsigned argStackSlots = GET_ARGC(REGS.pc) + construct;
MOZ_ASSERT(REGS.stackDepth() >= 2u + GET_ARGC(REGS.pc));
CallArgs args = CallArgsFromSp(argStackSlots, REGS.sp, construct);
CallArgs args = CallArgsFromSp(argStackSlots, REGS.sp, construct, ignoresReturnValue);
JSFunction* maybeFun;
bool isFunction = IsFunctionObject(args.calleev(), &maybeFun);

View File

@ -2282,14 +2282,23 @@
* Operands:
* Stack: =>
*/ \
macro(JSOP_JUMPTARGET, 230, "jumptarget", NULL, 1, 0, 0, JOF_BYTE)
macro(JSOP_JUMPTARGET, 230, "jumptarget", NULL, 1, 0, 0, JOF_BYTE)\
/*
* Like JSOP_CALL, but tells the function that the return value is ignored.
* stack.
* Category: Statements
* Type: Function
* Operands: uint16_t argc
* Stack: callee, this, args[0], ..., args[argc-1] => rval
* nuses: (argc+2)
*/ \
macro(JSOP_CALL_IGNORES_RV, 231, "call-ignores-rv", NULL, 3, -1, 1, JOF_UINT16|JOF_INVOKE|JOF_TYPESET)
/*
* In certain circumstances it may be useful to "pad out" the opcode space to
* a power of two. Use this macro to do so.
*/
#define FOR_EACH_TRAILING_UNUSED_OPCODE(macro) \
macro(231) \
macro(232) \
macro(233) \
macro(234) \

View File

@ -2154,7 +2154,7 @@ static const JSFunctionSpec intrinsic_functions[] = {
JS_INLINABLE_FN("std_Array_slice", array_slice, 2,0, ArraySlice),
JS_FN("std_Array_sort", array_sort, 1,0),
JS_FN("std_Array_reverse", array_reverse, 0,0),
JS_INLINABLE_FN("std_Array_splice", array_splice, 2,0, ArraySplice),
JS_FNINFO("std_Array_splice", array_splice, &array_splice_info, 2,0),
JS_FN("std_Date_now", date_now, 0,0),
JS_FN("std_Date_valueOf", date_valueOf, 0,0),

View File

@ -1006,6 +1006,17 @@ class InvokeArgs : public detail::GenericArgsBase<NO_CONSTRUCT>
explicit InvokeArgs(JSContext* cx) : Base(cx) {}
};
/** Function call args of statically-unknown count. */
class InvokeArgsMaybeIgnoresReturnValue : public detail::GenericArgsBase<NO_CONSTRUCT>
{
using Base = detail::GenericArgsBase<NO_CONSTRUCT>;
public:
explicit InvokeArgsMaybeIgnoresReturnValue(JSContext* cx, bool ignoresReturnValue) : Base(cx) {
this->ignoresReturnValue_ = ignoresReturnValue;
}
};
/** Function call args of statically-known count. */
template <size_t N>
class FixedInvokeArgs : public detail::FixedArgsBase<NO_CONSTRUCT, N>