зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1700443: Remove argumentsOptimizationFailed r=jandem
Depends on D114032 Differential Revision: https://phabricator.services.mozilla.com/D114033
This commit is contained in:
Родитель
68468b3a61
Коммит
613c40a99c
|
@ -2094,12 +2094,6 @@ bool jit::FinishBailoutToBaseline(BaselineBailoutInfo* bailoutInfoArg) {
|
|||
saveFailedICHash = true;
|
||||
break;
|
||||
|
||||
case BailoutKind::NotOptimizedArgumentsGuard:
|
||||
// Optimized-arguments escaped to a slow path. Disable the optimization to
|
||||
// prevent bailout loops.
|
||||
JSScript::argumentsOptimizationFailed(cx, innerScript);
|
||||
break;
|
||||
|
||||
case BailoutKind::UninitializedLexical:
|
||||
HandleLexicalCheckFailure(cx, outerScript, innerScript);
|
||||
break;
|
||||
|
|
|
@ -853,23 +853,11 @@ bool DoGetElemFallback(JSContext* cx, BaselineFrame* frame,
|
|||
MOZ_ASSERT(JSOp(*pc) == JSOp::GetElem);
|
||||
#endif
|
||||
|
||||
// Don't pass lhs directly, we need it when generating stubs.
|
||||
RootedValue lhsCopy(cx, lhs);
|
||||
|
||||
bool isOptimizedArgs = false;
|
||||
if (lhs.isMagic(JS_OPTIMIZED_ARGUMENTS)) {
|
||||
// Handle optimized arguments[i] access.
|
||||
isOptimizedArgs =
|
||||
MaybeGetElemOptimizedArguments(cx, frame, &lhsCopy, rhs, res);
|
||||
}
|
||||
|
||||
TryAttachStub<GetPropIRGenerator>("GetElem", cx, frame, stub,
|
||||
CacheKind::GetElem, lhs, rhs);
|
||||
|
||||
if (!isOptimizedArgs) {
|
||||
if (!GetElementOperation(cx, lhsCopy, rhs, res)) {
|
||||
return false;
|
||||
}
|
||||
if (!GetElementOperation(cx, lhs, rhs, res)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -1763,12 +1751,6 @@ bool DoCallFallback(JSContext* cx, BaselineFrame* frame, ICCall_Fallback* stub,
|
|||
RootedValue callee(cx, vp[0]);
|
||||
RootedValue newTarget(cx, constructing ? callArgs.newTarget() : NullValue());
|
||||
|
||||
// Handle funapply with JSOp::Arguments
|
||||
if (op == JSOp::FunApply && argc == 2 &&
|
||||
callArgs[1].isMagic(JS_OPTIMIZED_ARGUMENTS)) {
|
||||
GuardFunApplyArgumentsOptimization(cx, frame, callArgs);
|
||||
}
|
||||
|
||||
// Transition stub state to megamorphic or generic if warranted.
|
||||
MaybeTransition(cx, frame, stub);
|
||||
|
||||
|
|
|
@ -4620,15 +4620,6 @@ void CodeGenerator::visitGuardValue(LGuardValue* lir) {
|
|||
bailoutFrom(&bail, lir->snapshot());
|
||||
}
|
||||
|
||||
void CodeGenerator::visitGuardNotOptimizedArguments(
|
||||
LGuardNotOptimizedArguments* lir) {
|
||||
ValueOperand input = ToValue(lir, LGuardNotOptimizedArguments::Input);
|
||||
Label bail;
|
||||
masm.branchTestValue(Assembler::Equal, input,
|
||||
MagicValue(JS_OPTIMIZED_ARGUMENTS), &bail);
|
||||
bailoutFrom(&bail, lir->snapshot());
|
||||
}
|
||||
|
||||
void CodeGenerator::visitGuardNullOrUndefined(LGuardNullOrUndefined* lir) {
|
||||
ValueOperand input = ToValue(lir, LGuardNullOrUndefined::Input);
|
||||
|
||||
|
|
|
@ -81,8 +81,7 @@ static const SnapshotOffset INVALID_SNAPSHOT_OFFSET = uint32_t(-1);
|
|||
*
|
||||
* 2. If the bailout occurs because an assumption we made in WarpBuilder was
|
||||
* invalidated, then FinishBailoutToBaseline will set a flag on the script
|
||||
* to avoid that assumption in the future. Examples include
|
||||
* NotOptimizedArgumentsGuard and UninitializedLexical.
|
||||
* to avoid that assumption in the future: for example, UninitializedLexical.
|
||||
*
|
||||
* 3. Similarly, if the bailing instruction is generated or modified by a MIR
|
||||
* optimization, then FinishBailoutToBaseline will set a flag on the script
|
||||
|
@ -162,10 +161,6 @@ enum class BailoutKind : uint8_t {
|
|||
// We hit this code for the first time.
|
||||
FirstExecution,
|
||||
|
||||
// A bailout triggered by MGuardNotOptimizedArguments. We will call
|
||||
// argumentsOptimizationFailed to invalidate the script.
|
||||
NotOptimizedArgumentsGuard,
|
||||
|
||||
// A lexical check failed. We will set lexical checks as unmovable.
|
||||
UninitializedLexical,
|
||||
|
||||
|
@ -208,8 +203,6 @@ inline const char* BailoutKindString(BailoutKind kind) {
|
|||
return "Debugger";
|
||||
case BailoutKind::FirstExecution:
|
||||
return "FirstExecution";
|
||||
case BailoutKind::NotOptimizedArgumentsGuard:
|
||||
return "NotOptimizedArgumentsGuard";
|
||||
case BailoutKind::UninitializedLexical:
|
||||
return "UninitializedLexical";
|
||||
case BailoutKind::IonExceptionDebugMode:
|
||||
|
|
|
@ -4214,15 +4214,6 @@ void LIRGenerator::visitGuardValue(MGuardValue* ins) {
|
|||
redefine(ins, ins->value());
|
||||
}
|
||||
|
||||
void LIRGenerator::visitGuardNotOptimizedArguments(
|
||||
MGuardNotOptimizedArguments* ins) {
|
||||
MOZ_ASSERT(ins->value()->type() == MIRType::Value);
|
||||
auto* lir = new (alloc()) LGuardNotOptimizedArguments(useBox(ins->value()));
|
||||
assignSnapshot(lir, ins->bailoutKind());
|
||||
add(lir, ins);
|
||||
redefine(ins, ins->value());
|
||||
}
|
||||
|
||||
void LIRGenerator::visitGuardNullOrUndefined(MGuardNullOrUndefined* ins) {
|
||||
MOZ_ASSERT(ins->value()->type() == MIRType::Value);
|
||||
auto* lir = new (alloc()) LGuardNullOrUndefined(useBox(ins->value()));
|
||||
|
|
|
@ -5124,31 +5124,6 @@ MDefinition* MGuardValue::foldsTo(TempAllocator& alloc) {
|
|||
return this;
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool MGuardNotOptimizedArguments::maybeIsOptimizedArguments(MDefinition* def) {
|
||||
if (def->isBox()) {
|
||||
def = def->toBox()->input();
|
||||
}
|
||||
|
||||
if (def->type() != MIRType::Value &&
|
||||
def->type() != MIRType::MagicOptimizedArguments) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Only phis and constants can produce optimized-arguments.
|
||||
MOZ_ASSERT_IF(def->type() == MIRType::MagicOptimizedArguments,
|
||||
def->isConstant());
|
||||
return def->isConstant() || def->isPhi();
|
||||
}
|
||||
|
||||
MDefinition* MGuardNotOptimizedArguments::foldsTo(TempAllocator& alloc) {
|
||||
if (!maybeIsOptimizedArguments(value())) {
|
||||
return value();
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
MDefinition* MGuardNullOrUndefined::foldsTo(TempAllocator& alloc) {
|
||||
MDefinition* input = value();
|
||||
if (input->isBox()) {
|
||||
|
|
|
@ -9282,36 +9282,6 @@ class MGuardValue : public MUnaryInstruction, public BoxInputsPolicy::Data {
|
|||
AliasSet getAliasSet() const override { return AliasSet::None(); }
|
||||
};
|
||||
|
||||
// Guards the value is not MagicValue(JS_OPTIMIZED_ARGUMENTS). If this fails,
|
||||
// disable lazy arguments for the script.
|
||||
class MGuardNotOptimizedArguments : public MUnaryInstruction,
|
||||
public BoxInputsPolicy::Data {
|
||||
explicit MGuardNotOptimizedArguments(MDefinition* val)
|
||||
: MUnaryInstruction(classOpcode, val) {
|
||||
setGuard();
|
||||
setResultType(MIRType::Value);
|
||||
// Note: don't setMovable() to not deoptimize lazy arguments unnecessarily.
|
||||
|
||||
// If this instruction bails out, we will disable the optimization to
|
||||
// prevent bailout loops.
|
||||
setBailoutKind(BailoutKind::NotOptimizedArgumentsGuard);
|
||||
}
|
||||
|
||||
public:
|
||||
INSTRUCTION_HEADER(GuardNotOptimizedArguments)
|
||||
TRIVIAL_NEW_WRAPPERS
|
||||
NAMED_OPERANDS((0, value))
|
||||
|
||||
bool congruentTo(const MDefinition* ins) const override {
|
||||
return congruentIfOperandsEqual(ins);
|
||||
}
|
||||
|
||||
static bool maybeIsOptimizedArguments(MDefinition* def);
|
||||
|
||||
MDefinition* foldsTo(TempAllocator& alloc) override;
|
||||
AliasSet getAliasSet() const override { return AliasSet::None(); }
|
||||
};
|
||||
|
||||
// Guard on null or undefined values.
|
||||
class MGuardNullOrUndefined : public MUnaryInstruction,
|
||||
public BoxInputsPolicy::Data {
|
||||
|
|
|
@ -1805,11 +1805,6 @@ bool WarpBuilder::buildCallOp(BytecodeLocation loc) {
|
|||
needsThisCheck = true;
|
||||
}
|
||||
|
||||
if (op == JSOp::FunApply && argc == 2) {
|
||||
MDefinition* arg = maybeGuardNotOptimizedArguments(callInfo.getArg(1));
|
||||
callInfo.setArg(1, arg);
|
||||
}
|
||||
|
||||
MCall* call = makeCall(callInfo, needsThisCheck);
|
||||
if (!call) {
|
||||
return false;
|
||||
|
@ -3191,12 +3186,6 @@ bool WarpBuilder::buildIC(BytecodeLocation loc, CacheKind kind,
|
|||
PropertyName* name = loc.getPropertyName(script_);
|
||||
MConstant* id = constant(StringValue(name));
|
||||
MDefinition* val = getInput(0);
|
||||
if (info().hasArguments()) {
|
||||
const JSAtomState& names = mirGen().runtime->names();
|
||||
if (name == names.length || name == names.callee) {
|
||||
val = maybeGuardNotOptimizedArguments(val);
|
||||
}
|
||||
}
|
||||
auto* ins = MGetPropertyCache::New(alloc(), val, id);
|
||||
current->add(ins);
|
||||
current->push(ins);
|
||||
|
@ -3204,7 +3193,7 @@ bool WarpBuilder::buildIC(BytecodeLocation loc, CacheKind kind,
|
|||
}
|
||||
case CacheKind::GetElem: {
|
||||
MOZ_ASSERT(numInputs == 2);
|
||||
MDefinition* val = maybeGuardNotOptimizedArguments(getInput(0));
|
||||
MDefinition* val = getInput(0);
|
||||
auto* ins = MGetPropertyCache::New(alloc(), val, getInput(1));
|
||||
current->add(ins);
|
||||
current->push(ins);
|
||||
|
@ -3342,31 +3331,6 @@ bool WarpBuilder::buildBailoutForColdIC(BytecodeLocation loc, CacheKind kind) {
|
|||
return true;
|
||||
}
|
||||
|
||||
MDefinition* WarpBuilder::maybeGuardNotOptimizedArguments(MDefinition* def) {
|
||||
// The arguments-analysis ensures the optimized-arguments MagicValue can only
|
||||
// flow into a few allowlisted JSOps, for instance arguments.length or
|
||||
// arguments[i]. See ArgumentsUseCanBeLazy.
|
||||
//
|
||||
// Baseline ICs have fast paths for these optimized-arguments uses and the
|
||||
// transpiler lets us generate MIR for them. Ion ICs, however, don't support
|
||||
// optimized-arguments because it's hard/impossible to access frame values
|
||||
// reliably from Ion ICs, especially in inlined functions. To deal with this,
|
||||
// we insert MGuardNotOptimizedArguments here if needed. On bailout we
|
||||
// deoptimize the arguments-analysis.
|
||||
|
||||
if (!info().hasArguments() || info().needsArgsObj() || info().isAnalysis()) {
|
||||
return def;
|
||||
}
|
||||
|
||||
if (!MGuardNotOptimizedArguments::maybeIsOptimizedArguments(def)) {
|
||||
return def;
|
||||
}
|
||||
|
||||
auto* ins = MGuardNotOptimizedArguments::New(alloc(), def);
|
||||
current->add(ins);
|
||||
return ins;
|
||||
}
|
||||
|
||||
class MOZ_RAII AutoAccumulateReturns {
|
||||
MIRGraph& graph_;
|
||||
MIRGraphReturns* prev_;
|
||||
|
|
|
@ -285,8 +285,6 @@ class MOZ_STACK_CLASS WarpBuilder : public WarpBuilderShared {
|
|||
MConstant* globalLexicalEnvConstant();
|
||||
MDefinition* getCallee();
|
||||
|
||||
MDefinition* maybeGuardNotOptimizedArguments(MDefinition* def);
|
||||
|
||||
[[nodiscard]] bool buildUnaryOp(BytecodeLocation loc);
|
||||
[[nodiscard]] bool buildBinaryOp(BytecodeLocation loc);
|
||||
[[nodiscard]] bool buildCompareOp(BytecodeLocation loc);
|
||||
|
|
|
@ -8109,19 +8109,6 @@ class LGuardValue : public LInstructionHelper<0, BOX_PIECES, 0> {
|
|||
MGuardValue* mir() { return mir_->toGuardValue(); }
|
||||
};
|
||||
|
||||
class LGuardNotOptimizedArguments
|
||||
: public LInstructionHelper<0, BOX_PIECES, 0> {
|
||||
public:
|
||||
LIR_HEADER(GuardNotOptimizedArguments)
|
||||
|
||||
explicit LGuardNotOptimizedArguments(const LBoxAllocation& input)
|
||||
: LInstructionHelper(classOpcode) {
|
||||
setBoxOperand(Input, input);
|
||||
}
|
||||
|
||||
static const size_t Input = 0;
|
||||
};
|
||||
|
||||
class LGuardNullOrUndefined : public LInstructionHelper<0, BOX_PIECES, 0> {
|
||||
public:
|
||||
LIR_HEADER(GuardNullOrUndefined)
|
||||
|
|
|
@ -47,23 +47,6 @@ static inline bool IsOptimizedArguments(AbstractFramePtr frame,
|
|||
return vp.isMagic(JS_OPTIMIZED_ARGUMENTS);
|
||||
}
|
||||
|
||||
/*
|
||||
* One optimized consumer of MagicValue(JS_OPTIMIZED_ARGUMENTS) is f.apply.
|
||||
* However, this speculation must be guarded before calling 'apply' in case it
|
||||
* is not the builtin Function.prototype.apply.
|
||||
*/
|
||||
static inline void GuardFunApplyArgumentsOptimization(JSContext* cx,
|
||||
AbstractFramePtr frame,
|
||||
CallArgs& args) {
|
||||
if (args.length() == 2 && IsOptimizedArguments(frame, args[1])) {
|
||||
if (!IsNativeFunction(args.calleev(), js::fun_apply)) {
|
||||
RootedScript script(cx, frame.script());
|
||||
JSScript::argumentsOptimizationFailed(cx, script);
|
||||
args[1].setObject(frame.argsObj());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Per ES6, lexical declarations may not be accessed in any fashion until they
|
||||
* are initialized (i.e., until the actual declaring statement is
|
||||
|
@ -513,27 +496,6 @@ static MOZ_ALWAYS_INLINE bool GetPrimitiveElementOperation(
|
|||
return true;
|
||||
}
|
||||
|
||||
static MOZ_ALWAYS_INLINE bool MaybeGetElemOptimizedArguments(
|
||||
JSContext* cx, AbstractFramePtr frame, MutableHandleValue lref,
|
||||
HandleValue rref, MutableHandleValue res) {
|
||||
if (IsOptimizedArguments(frame, lref)) {
|
||||
if (rref.isInt32()) {
|
||||
int32_t i = rref.toInt32();
|
||||
if (i >= 0 && uint32_t(i) < frame.numActualArgs()) {
|
||||
res.set(frame.unaliasedActual(i));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
RootedScript script(cx, frame.script());
|
||||
JSScript::argumentsOptimizationFailed(cx, script);
|
||||
|
||||
lref.set(ObjectValue(frame.argsObj()));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static MOZ_ALWAYS_INLINE bool GetElementOperationWithStackIndex(
|
||||
JSContext* cx, HandleValue lref, int lrefIndex, HandleValue rref,
|
||||
MutableHandleValue res) {
|
||||
|
|
|
@ -3048,14 +3048,8 @@ static MOZ_NEVER_INLINE JS_HAZ_JSNATIVE_CALLER bool Interpret(JSContext* cx,
|
|||
HandleValue rval = REGS.stackHandleAt(-1);
|
||||
MutableHandleValue res = REGS.stackHandleAt(-2);
|
||||
|
||||
bool done =
|
||||
MaybeGetElemOptimizedArguments(cx, REGS.fp(), lval, rval, res);
|
||||
|
||||
if (!done) {
|
||||
if (!GetElementOperationWithStackIndex(cx, lval, lvalIndex, rval,
|
||||
res)) {
|
||||
goto error;
|
||||
}
|
||||
if (!GetElementOperationWithStackIndex(cx, lval, lvalIndex, rval, res)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
REGS.sp--;
|
||||
|
@ -3186,18 +3180,13 @@ static MOZ_NEVER_INLINE JS_HAZ_JSNATIVE_CALLER bool Interpret(JSContext* cx,
|
|||
}
|
||||
END_CASE(SpreadCall)
|
||||
|
||||
CASE(FunApply) {
|
||||
CallArgs args = CallArgsFromSp(GET_ARGC(REGS.pc), REGS.sp);
|
||||
GuardFunApplyArgumentsOptimization(cx, REGS.fp(), args);
|
||||
/* FALL THROUGH */
|
||||
}
|
||||
|
||||
CASE(New)
|
||||
CASE(Call)
|
||||
CASE(CallIgnoresRv)
|
||||
CASE(CallIter)
|
||||
CASE(SuperCall)
|
||||
CASE(FunCall) {
|
||||
CASE(FunCall)
|
||||
CASE(FunApply) {
|
||||
static_assert(JSOpLength_Call == JSOpLength_New,
|
||||
"call and new must be the same size");
|
||||
static_assert(JSOpLength_Call == JSOpLength_CallIgnoresRv,
|
||||
|
|
|
@ -4719,72 +4719,6 @@ void js::SetFrameArgumentsObject(JSContext* cx, AbstractFramePtr frame,
|
|||
}
|
||||
}
|
||||
|
||||
/* static */
|
||||
void JSScript::argumentsOptimizationFailed(JSContext* cx, HandleScript script) {
|
||||
MOZ_ASSERT(script->isFunction());
|
||||
MOZ_ASSERT(script->argumentsHasVarBinding());
|
||||
|
||||
/*
|
||||
* It is possible that the arguments optimization has already failed,
|
||||
* everything has been fixed up, but there was an outstanding magic value
|
||||
* on the stack that has just now flowed into an apply. In this case, there
|
||||
* is nothing to do; GuardFunApplySpeculation will patch in the real
|
||||
* argsobj.
|
||||
*/
|
||||
if (script->needsArgsObj()) {
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(!script->isGenerator());
|
||||
MOZ_ASSERT(!script->isAsync());
|
||||
|
||||
script->setFlag(MutableFlags::NeedsArgsObj);
|
||||
|
||||
// Warp code depends on the NeedsArgsObj flag so invalidate the script
|
||||
// (including compilations inlining the script).
|
||||
{
|
||||
jit::RecompileInfoVector invalid;
|
||||
AddPendingInvalidation(invalid, script);
|
||||
Invalidate(cx, invalid);
|
||||
}
|
||||
|
||||
/*
|
||||
* By design, the arguments optimization is only made when there are no
|
||||
* outstanding cases of MagicValue(JS_OPTIMIZED_ARGUMENTS) at any points
|
||||
* where the optimization could fail, other than an active invocation of
|
||||
* 'f.apply(x, arguments)'. Thus, there are no outstanding values of
|
||||
* MagicValue(JS_OPTIMIZED_ARGUMENTS) on the stack. However, there are
|
||||
* three things that need fixup:
|
||||
* - there may be any number of activations of this script that don't have
|
||||
* an argsObj that now need one.
|
||||
* - jit code compiled (and possible active on the stack) with the static
|
||||
* assumption of !script->needsArgsObj();
|
||||
* - type inference data for the script assuming script->needsArgsObj
|
||||
*/
|
||||
for (AllScriptFramesIter i(cx); !i.done(); ++i) {
|
||||
/*
|
||||
* We cannot reliably create an arguments object for Ion activations of
|
||||
* this script. To maintain the invariant that "script->needsArgsObj
|
||||
* implies fp->hasArgsObj", the Ion bail mechanism will create an
|
||||
* arguments object right after restoring the BaselineFrame and before
|
||||
* entering Baseline code (in jit::FinishBailoutToBaseline).
|
||||
*/
|
||||
if (i.isIon()) {
|
||||
continue;
|
||||
}
|
||||
AbstractFramePtr frame = i.abstractFramePtr();
|
||||
if (frame.isFunctionFrame() && frame.script() == script) {
|
||||
/* We crash on OOM since cleaning up here would be complicated. */
|
||||
AutoEnterOOMUnsafeRegion oomUnsafe;
|
||||
ArgumentsObject* argsobj = ArgumentsObject::createExpected(cx, frame);
|
||||
if (!argsobj) {
|
||||
oomUnsafe.crash("JSScript::argumentsOptimizationFailed");
|
||||
}
|
||||
SetFrameArgumentsObject(cx, frame, script, argsobj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool JSScript::formalIsAliased(unsigned argSlot) {
|
||||
if (functionHasParameterExprs()) {
|
||||
return false;
|
||||
|
|
|
@ -2020,8 +2020,6 @@ class JSScript : public js::BaseScript {
|
|||
}
|
||||
|
||||
bool needsArgsObj() const { return hasFlag(MutableFlags::NeedsArgsObj); }
|
||||
static void argumentsOptimizationFailed(JSContext* cx,
|
||||
js::HandleScript script);
|
||||
|
||||
/*
|
||||
* Arguments access (via JSOp::*Arg* opcodes) must access the canonical
|
||||
|
|
Загрузка…
Ссылка в новой задаче