mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-09 11:25:00 +00:00
Bug 716647 - Part 1: Introduce JS_OPTIMIZED_OUT magic for optimized out slots and teach Debugger about them. (r=jandem)
This commit is contained in:
parent
b561e124dd
commit
df7ee101da
@ -237,6 +237,7 @@ typedef enum JSWhyMagic
|
||||
JS_HASH_KEY_EMPTY, /* see class js::HashableValue */
|
||||
JS_ION_ERROR, /* error while running Ion code */
|
||||
JS_ION_BAILOUT, /* status code to signal EnterIon will OSR into Interpret */
|
||||
JS_OPTIMIZED_OUT, /* optimized out slot */
|
||||
JS_GENERIC_MAGIC /* for local use */
|
||||
} JSWhyMagic;
|
||||
|
||||
@ -1278,6 +1279,16 @@ IsPoisonedValue(const Value &v)
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool
|
||||
IsOptimizedPlaceholderMagicValue(const Value &v)
|
||||
{
|
||||
if (v.isMagic()) {
|
||||
MOZ_ASSERT(v.whyMagic() == JS_OPTIMIZED_ARGUMENTS || v.whyMagic() == JS_OPTIMIZED_OUT);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
|
||||
static inline Value
|
||||
|
@ -588,7 +588,7 @@ InitFromBailout(JSContext *cx, HandleScript caller, jsbytecode *callerPC,
|
||||
if (fun && fun->isHeavyweight())
|
||||
flags |= BaselineFrame::HAS_CALL_OBJ;
|
||||
} else {
|
||||
JS_ASSERT(v.isUndefined());
|
||||
JS_ASSERT(v.isUndefined() || v.isMagic(JS_OPTIMIZED_OUT));
|
||||
|
||||
// Get scope chain from function or script.
|
||||
if (fun) {
|
||||
@ -617,7 +617,7 @@ InitFromBailout(JSContext *cx, HandleScript caller, jsbytecode *callerPC,
|
||||
// If script maybe has an arguments object, the third slot will hold it.
|
||||
if (script->argumentsHasVarBinding()) {
|
||||
v = iter.read();
|
||||
JS_ASSERT(v.isObject() || v.isUndefined());
|
||||
JS_ASSERT(v.isObject() || v.isUndefined() || v.isMagic(JS_OPTIMIZED_OUT));
|
||||
if (v.isObject())
|
||||
argsObj = &v.toObject().as<ArgumentsObject>();
|
||||
}
|
||||
|
@ -137,15 +137,15 @@ jit::EliminateDeadResumePointOperands(MIRGenerator *mir, MIRGraph &graph)
|
||||
continue;
|
||||
}
|
||||
|
||||
// Store an undefined value in place of all dead resume point
|
||||
// operands. Making any such substitution can in general alter
|
||||
// the interpreter's behavior, even though the code is dead, as
|
||||
// the interpreter will still execute opcodes whose effects
|
||||
// cannot be observed. If the undefined value were to flow to,
|
||||
// say, a dead property access the interpreter could throw an
|
||||
// exception; we avoid this problem by removing dead operands
|
||||
// before removing dead code.
|
||||
MConstant *constant = MConstant::New(graph.alloc(), UndefinedValue());
|
||||
// Store an optimized out magic value in place of all dead
|
||||
// resume point operands. Making any such substitution can in
|
||||
// general alter the interpreter's behavior, even though the
|
||||
// code is dead, as the interpreter will still execute opcodes
|
||||
// whose effects cannot be observed. If the undefined value
|
||||
// were to flow to, say, a dead property access the
|
||||
// interpreter could throw an exception; we avoid this problem
|
||||
// by removing dead operands before removing dead code.
|
||||
MConstant *constant = MConstant::New(graph.alloc(), MagicValue(JS_OPTIMIZED_OUT));
|
||||
block->insertBefore(*(block->begin()), constant);
|
||||
uses = mrp->replaceOperand(uses, constant);
|
||||
}
|
||||
@ -443,7 +443,9 @@ static MIRType
|
||||
GuessPhiType(MPhi *phi, bool *hasInputsWithEmptyTypes)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
// Check that different magic constants aren't flowing together.
|
||||
// Check that different magic constants aren't flowing together. Ignore
|
||||
// JS_OPTIMIZED_OUT, since an operand could be legitimately optimized
|
||||
// away.
|
||||
MIRType magicType = MIRType_None;
|
||||
for (size_t i = 0; i < phi->numOperands(); i++) {
|
||||
MDefinition *in = phi->getOperand(i);
|
||||
@ -733,6 +735,9 @@ TypeAnalyzer::replaceRedundantPhi(MPhi *phi)
|
||||
case MIRType_MagicOptimizedArguments:
|
||||
v = MagicValue(JS_OPTIMIZED_ARGUMENTS);
|
||||
break;
|
||||
case MIRType_MagicOptimizedOut:
|
||||
v = MagicValue(JS_OPTIMIZED_OUT);
|
||||
break;
|
||||
default:
|
||||
MOZ_ASSUME_UNREACHABLE("unexpected type");
|
||||
}
|
||||
@ -755,7 +760,8 @@ TypeAnalyzer::insertConversions()
|
||||
for (MPhiIterator phi(block->phisBegin()); phi != block->phisEnd();) {
|
||||
if (phi->type() == MIRType_Undefined ||
|
||||
phi->type() == MIRType_Null ||
|
||||
phi->type() == MIRType_MagicOptimizedArguments)
|
||||
phi->type() == MIRType_MagicOptimizedArguments ||
|
||||
phi->type() == MIRType_MagicOptimizedOut)
|
||||
{
|
||||
replaceRedundantPhi(*phi);
|
||||
phi = block->discardPhiAt(phi);
|
||||
|
@ -66,7 +66,7 @@ jit::NewBaselineFrameInspector(TempAllocator *temp, BaselineFrame *frame, Compil
|
||||
// during compilation could capture nursery pointers, so the values' types
|
||||
// are recorded instead.
|
||||
|
||||
inspector->thisType = types::GetValueType(frame->thisValue());
|
||||
inspector->thisType = types::GetMaybeOptimizedOutValueType(frame->thisValue());
|
||||
|
||||
if (frame->scopeChain()->hasSingletonType())
|
||||
inspector->singletonScopeChain = frame->scopeChain();
|
||||
@ -77,24 +77,29 @@ jit::NewBaselineFrameInspector(TempAllocator *temp, BaselineFrame *frame, Compil
|
||||
if (!inspector->argTypes.reserve(frame->numFormalArgs()))
|
||||
return nullptr;
|
||||
for (size_t i = 0; i < frame->numFormalArgs(); i++) {
|
||||
if (script->formalIsAliased(i))
|
||||
if (script->formalIsAliased(i)) {
|
||||
inspector->argTypes.infallibleAppend(types::Type::UndefinedType());
|
||||
else if (!script->argsObjAliasesFormals())
|
||||
inspector->argTypes.infallibleAppend(types::GetValueType(frame->unaliasedFormal(i)));
|
||||
else if (frame->hasArgsObj())
|
||||
inspector->argTypes.infallibleAppend(types::GetValueType(frame->argsObj().arg(i)));
|
||||
else
|
||||
} else if (!script->argsObjAliasesFormals()) {
|
||||
types::Type type = types::GetMaybeOptimizedOutValueType(frame->unaliasedFormal(i));
|
||||
inspector->argTypes.infallibleAppend(type);
|
||||
} else if (frame->hasArgsObj()) {
|
||||
types::Type type = types::GetMaybeOptimizedOutValueType(frame->argsObj().arg(i));
|
||||
inspector->argTypes.infallibleAppend(type);
|
||||
} else {
|
||||
inspector->argTypes.infallibleAppend(types::Type::UndefinedType());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!inspector->varTypes.reserve(frame->script()->nfixed()))
|
||||
return nullptr;
|
||||
for (size_t i = 0; i < frame->script()->nfixed(); i++) {
|
||||
if (info->isSlotAliasedAtOsr(i + info->firstLocalSlot()))
|
||||
if (info->isSlotAliasedAtOsr(i + info->firstLocalSlot())) {
|
||||
inspector->varTypes.infallibleAppend(types::Type::UndefinedType());
|
||||
else
|
||||
inspector->varTypes.infallibleAppend(types::GetValueType(frame->unaliasedLocal(i)));
|
||||
} else {
|
||||
types::Type type = types::GetMaybeOptimizedOutValueType(frame->unaliasedLocal(i));
|
||||
inspector->varTypes.infallibleAppend(type);
|
||||
}
|
||||
}
|
||||
|
||||
return inspector;
|
||||
|
@ -91,6 +91,7 @@ enum MIRType
|
||||
MIRType_String,
|
||||
MIRType_Object,
|
||||
MIRType_MagicOptimizedArguments, // JS_OPTIMIZED_ARGUMENTS magic value.
|
||||
MIRType_MagicOptimizedOut, // JS_OPTIMIZED_OUT magic value.
|
||||
MIRType_MagicHole, // JS_ELEMENTS_HOLE magic value.
|
||||
MIRType_MagicIsConstructing, // JS_IS_CONSTRUCTING magic value.
|
||||
MIRType_Value,
|
||||
@ -164,6 +165,7 @@ ValueTypeFromMIRType(MIRType type)
|
||||
case MIRType_String:
|
||||
return JSVAL_TYPE_STRING;
|
||||
case MIRType_MagicOptimizedArguments:
|
||||
case MIRType_MagicOptimizedOut:
|
||||
case MIRType_MagicHole:
|
||||
case MIRType_MagicIsConstructing:
|
||||
return JSVAL_TYPE_MAGIC;
|
||||
@ -201,6 +203,8 @@ StringFromMIRType(MIRType type)
|
||||
return "Object";
|
||||
case MIRType_MagicOptimizedArguments:
|
||||
return "MagicOptimizedArguments";
|
||||
case MIRType_MagicOptimizedOut:
|
||||
return "MagicOptimizedOut";
|
||||
case MIRType_MagicHole:
|
||||
return "MagicHole";
|
||||
case MIRType_MagicIsConstructing:
|
||||
|
@ -46,6 +46,8 @@ MIRType MIRTypeFromValue(const js::Value &vp)
|
||||
switch (vp.whyMagic()) {
|
||||
case JS_OPTIMIZED_ARGUMENTS:
|
||||
return MIRType_MagicOptimizedArguments;
|
||||
case JS_OPTIMIZED_OUT:
|
||||
return MIRType_MagicOptimizedOut;
|
||||
case JS_ELEMENTS_HOLE:
|
||||
return MIRType_MagicHole;
|
||||
case JS_IS_CONSTRUCTING:
|
||||
|
@ -150,7 +150,7 @@ CodeGeneratorShared::encodeAllocations(LSnapshot *snapshot, MResumePoint *resume
|
||||
mir = mir->toBox()->getOperand(0);
|
||||
|
||||
MIRType type = mir->isUnused()
|
||||
? MIRType_Undefined
|
||||
? MIRType_MagicOptimizedOut
|
||||
: mir->type();
|
||||
|
||||
RValueAllocation alloc;
|
||||
@ -194,9 +194,14 @@ CodeGeneratorShared::encodeAllocations(LSnapshot *snapshot, MResumePoint *resume
|
||||
break;
|
||||
}
|
||||
case MIRType_MagicOptimizedArguments:
|
||||
case MIRType_MagicOptimizedOut:
|
||||
{
|
||||
uint32_t index;
|
||||
if (!graph.addConstantToPool(MagicValue(JS_OPTIMIZED_ARGUMENTS), &index))
|
||||
JSWhyMagic why = (type == MIRType_MagicOptimizedArguments
|
||||
? JS_OPTIMIZED_ARGUMENTS
|
||||
: JS_OPTIMIZED_OUT);
|
||||
Value v = MagicValue(why);
|
||||
if (!graph.addConstantToPool(v, &index))
|
||||
return false;
|
||||
alloc = RValueAllocation::ConstantPool(index);
|
||||
break;
|
||||
|
@ -295,6 +295,13 @@ class Type
|
||||
/* Get the type of a jsval, or zero for an unknown special value. */
|
||||
inline Type GetValueType(const Value &val);
|
||||
|
||||
/*
|
||||
* Get the type of a possibly optimized out value. This generally only
|
||||
* happens on unconditional type monitors on bailing out of Ion, such as
|
||||
* for argument and local types.
|
||||
*/
|
||||
inline Type GetMaybeOptimizedOutValueType(const Value &val);
|
||||
|
||||
/*
|
||||
* Type inference memory management overview.
|
||||
*
|
||||
|
@ -110,6 +110,14 @@ GetValueType(const Value &val)
|
||||
return Type::PrimitiveType(val.extractNonDoubleType());
|
||||
}
|
||||
|
||||
inline Type
|
||||
GetMaybeOptimizedOutValueType(const Value &val)
|
||||
{
|
||||
if (val.isMagic() && val.whyMagic() == JS_OPTIMIZED_OUT)
|
||||
return Type::UndefinedType();
|
||||
return GetValueType(val);
|
||||
}
|
||||
|
||||
inline TypeFlags
|
||||
PrimitiveTypeFlag(JSValueType type)
|
||||
{
|
||||
|
@ -5662,6 +5662,7 @@ dumpValue(const Value &v)
|
||||
case JS_NATIVE_ENUMERATE: fprintf(stderr, " native enumeration"); break;
|
||||
case JS_NO_ITER_VALUE: fprintf(stderr, " no iter value"); break;
|
||||
case JS_GENERATOR_CLOSING: fprintf(stderr, " generator closing"); break;
|
||||
case JS_OPTIMIZED_OUT: fprintf(stderr, " optimized out"); break;
|
||||
default: fprintf(stderr, " ?!"); break;
|
||||
}
|
||||
#endif
|
||||
|
@ -3481,10 +3481,13 @@ js::SetFrameArgumentsObject(JSContext *cx, AbstractFramePtr frame,
|
||||
pc += JSOP_ARGUMENTS_LENGTH;
|
||||
JS_ASSERT(*pc == JSOP_SETALIASEDVAR);
|
||||
|
||||
if (frame.callObj().as<ScopeObject>().aliasedVar(pc).isMagic(JS_OPTIMIZED_ARGUMENTS))
|
||||
// Note that here and below, it is insufficient to only check for
|
||||
// JS_OPTIMIZED_ARGUMENTS, as Ion could have optimized out the
|
||||
// arguments slot.
|
||||
if (IsOptimizedPlaceholderMagicValue(frame.callObj().as<ScopeObject>().aliasedVar(pc)))
|
||||
frame.callObj().as<ScopeObject>().setAliasedVar(cx, pc, cx->names().arguments, ObjectValue(*argsobj));
|
||||
} else {
|
||||
if (frame.unaliasedLocal(var).isMagic(JS_OPTIMIZED_ARGUMENTS))
|
||||
if (IsOptimizedPlaceholderMagicValue(frame.unaliasedLocal(var)))
|
||||
frame.unaliasedLocal(var) = ObjectValue(*argsobj);
|
||||
}
|
||||
}
|
||||
|
@ -146,6 +146,7 @@
|
||||
macro(objectWindow, objectWindow, "[object Window]") \
|
||||
macro(of, of, "of") \
|
||||
macro(offset, offset, "offset") \
|
||||
macro(optimizedOut, optimizedOut, "optimizedOut") \
|
||||
macro(outOfMemory, outOfMemory, "out of memory") \
|
||||
macro(parseFloat, parseFloat, "parseFloat") \
|
||||
macro(parseInt, parseInt, "parseInt") \
|
||||
|
@ -756,6 +756,19 @@ Debugger::wrapDebuggeeValue(JSContext *cx, MutableHandleValue vp)
|
||||
|
||||
vp.setObject(*dobj);
|
||||
}
|
||||
} else if (vp.isMagic()) {
|
||||
// Other magic values should not have escaped.
|
||||
MOZ_ASSERT(vp.whyMagic() == JS_OPTIMIZED_OUT);
|
||||
|
||||
RootedObject optObj(cx, NewBuiltinClassInstance(cx, &JSObject::class_));
|
||||
if (!optObj)
|
||||
return false;
|
||||
|
||||
RootedValue trueVal(cx, BooleanValue(true));
|
||||
if (!JSObject::defineProperty(cx, optObj, cx->names().optimizedOut, trueVal))
|
||||
return false;
|
||||
|
||||
vp.setObject(*optObj);
|
||||
} else if (!cx->compartment()->wrap(cx, vp)) {
|
||||
vp.setUndefined();
|
||||
return false;
|
||||
@ -5956,9 +5969,9 @@ JS_DefineDebuggerObject(JSContext *cx, HandleObject obj)
|
||||
if (!objectProto)
|
||||
return false;
|
||||
envProto = js_InitClass(cx, debugCtor, objProto, &DebuggerEnv_class,
|
||||
DebuggerEnv_construct, 0,
|
||||
DebuggerEnv_properties, DebuggerEnv_methods,
|
||||
nullptr, nullptr);
|
||||
DebuggerEnv_construct, 0,
|
||||
DebuggerEnv_properties, DebuggerEnv_methods,
|
||||
nullptr, nullptr);
|
||||
if (!envProto)
|
||||
return false;
|
||||
memoryProto = js_InitClass(cx, debugCtor, objProto, &DebuggerMemory::class_,
|
||||
|
@ -453,6 +453,9 @@ class Debugger : private mozilla::LinkedListElement<Debugger>
|
||||
*
|
||||
* If *vp is an object, this produces a (new or existing) Debugger.Object
|
||||
* wrapper for it. Otherwise this is the same as JSCompartment::wrap.
|
||||
*
|
||||
* If *vp is a magic JS_OPTIMIZED_OUT value, this produces a plain object
|
||||
* of the form { optimizedOut: true }.
|
||||
*/
|
||||
bool wrapDebuggeeValue(JSContext *cx, MutableHandleValue vp);
|
||||
|
||||
|
@ -1179,7 +1179,7 @@ class DebugScopeProxy : public BaseProxyHandler
|
||||
} else {
|
||||
/* The unaliased value has been lost to the debugger. */
|
||||
if (action == GET)
|
||||
vp.set(UndefinedValue());
|
||||
vp.set(MagicValue(JS_OPTIMIZED_OUT));
|
||||
}
|
||||
} else {
|
||||
JS_ASSERT(bi->kind() == Binding::ARGUMENT);
|
||||
@ -1208,7 +1208,7 @@ class DebugScopeProxy : public BaseProxyHandler
|
||||
} else {
|
||||
/* The unaliased value has been lost to the debugger. */
|
||||
if (action == GET)
|
||||
vp.set(UndefinedValue());
|
||||
vp.set(MagicValue(JS_OPTIMIZED_OUT));
|
||||
}
|
||||
|
||||
if (action == SET)
|
||||
|
Loading…
Reference in New Issue
Block a user