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:
Shu-yu Guo 2014-04-24 01:59:36 -07:00
parent b561e124dd
commit df7ee101da
15 changed files with 101 additions and 32 deletions

View File

@ -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

View File

@ -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>();
}

View File

@ -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);

View File

@ -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;

View File

@ -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:

View File

@ -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:

View File

@ -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;

View File

@ -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.
*

View File

@ -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)
{

View File

@ -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

View File

@ -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);
}
}

View File

@ -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") \

View File

@ -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_,

View File

@ -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);

View File

@ -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)