Bug 589199 - Support global lexicals in Ion. (r=jandem)

This commit is contained in:
Shu-yu Guo 2015-10-06 14:00:29 -07:00
Родитель 934bb0bb13
Коммит e168c18e3b
16 изменённых файлов: 203 добавлений и 36 удалений

Просмотреть файл

@ -706,14 +706,14 @@ InitFromBailout(JSContext* cx, HandleScript caller, jsbytecode* callerPC,
}
} else {
// For global scripts without a non-syntactic scope the scope
// chain is the script's global (Ion does not compile scripts
// with a non-syntactic global scope). Also note that it's
// invalid to resume into the prologue in this case because
// the prologue expects the scope chain in R1 for eval and
// global scripts.
// chain is the script's global lexical scope (Ion does not
// compile scripts with a non-syntactic global scope). Also
// note that it's invalid to resume into the prologue in this
// case because the prologue expects the scope chain in R1 for
// eval and global scripts.
MOZ_ASSERT(!script->isForEval());
MOZ_ASSERT(!script->hasNonSyntacticScope());
scopeChain = &(script->global());
scopeChain = &(script->global().lexicalScope());
}
}

Просмотреть файл

@ -16,11 +16,7 @@ using namespace js::jit;
bool
FrameInfo::init(TempAllocator& alloc)
{
// The minimum stack size is two. One slot is always needed for
// this/arguments type checks. Two slots are needed because INITGLEXICAL
// (stack depth 1) is compiled as a SETPROP (stack depth 2) on the global
// lexical scope.
size_t nstack = Max(script->nslots() - script->nfixed(), size_t(2));
size_t nstack = Max(script->nslots() - script->nfixed(), size_t(MinJITStackSize));
if (!stack.init(alloc, nstack))
return false;

Просмотреть файл

@ -167,8 +167,6 @@ BytecodeAnalysis::init(TempAllocator& alloc, GSNCache& gsn)
case JSOP_LAMBDA_ARROW:
case JSOP_DEFFUN:
case JSOP_DEFVAR:
case JSOP_DEFCONST:
case JSOP_SETCONST:
usesScopeChain_ = true;
break;

Просмотреть файл

@ -3713,9 +3713,8 @@ CodeGenerator::visitCheckOverRecursed(LCheckOverRecursed* lir)
masm.bind(ool->rejoin());
}
typedef bool (*DefVarOrConstFn)(JSContext*, HandlePropertyName, unsigned, HandleObject);
static const VMFunction DefVarOrConstInfo =
FunctionInfo<DefVarOrConstFn>(DefVarOrConst);
typedef bool (*DefVarFn)(JSContext*, HandlePropertyName, unsigned, HandleObject);
static const VMFunction DefVarInfo = FunctionInfo<DefVarFn>(DefVar);
void
CodeGenerator::visitDefVar(LDefVar* lir)
@ -3726,7 +3725,19 @@ CodeGenerator::visitDefVar(LDefVar* lir)
pushArg(Imm32(lir->mir()->attrs())); // unsigned
pushArg(ImmGCPtr(lir->mir()->name())); // PropertyName*
callVM(DefVarOrConstInfo, lir);
callVM(DefVarInfo, lir);
}
typedef bool (*DefLexicalFn)(JSContext*, HandlePropertyName, unsigned);
static const VMFunction DefLexicalInfo = FunctionInfo<DefLexicalFn>(DefLexicalOperation);
void
CodeGenerator::visitDefLexical(LDefLexical* lir)
{
pushArg(Imm32(lir->mir()->attrs())); // unsigned
pushArg(ImmGCPtr(lir->mir()->name())); // PropertyName*
callVM(DefLexicalInfo, lir);
}
typedef bool (*DefFunOperationFn)(JSContext*, HandleScript, HandleObject, HandleFunction);

Просмотреть файл

@ -73,6 +73,7 @@ class CodeGenerator : public CodeGeneratorSpecific
void visitStart(LStart* lir);
void visitReturn(LReturn* ret);
void visitDefVar(LDefVar* lir);
void visitDefLexical(LDefLexical* lir);
void visitDefFun(LDefFun* lir);
void visitOsrEntry(LOsrEntry* lir);
void visitOsrScopeChain(LOsrScopeChain* lir);

Просмотреть файл

@ -221,7 +221,7 @@ class CompileInfo
nbodyfixed_ = script->nbodyfixed();
nlocals_ = script->nfixed();
fixedLexicalBegin_ = script->fixedLexicalBegin();
nstack_ = script->nslots() - script->nfixed();
nstack_ = Max<unsigned>(script->nslots() - script->nfixed(), MinJITStackSize);
nslots_ = nimplicit_ + nargs_ + nlocals_ + nstack_;
}

Просмотреть файл

@ -1235,10 +1235,10 @@ IonBuilder::initScopeChain(MDefinition* callee)
scope = constant(ObjectValue(module->initialEnvironment()));
} else {
// For global scripts without a non-syntactic global scope, the scope
// chain is the global object.
// chain is the global lexical scope.
MOZ_ASSERT(!script()->isForEval());
MOZ_ASSERT(!script()->hasNonSyntacticScope());
scope = constant(ObjectValue(script()->global()));
scope = constant(ObjectValue(script()->global().lexicalScope()));
}
current->setScopeChain(scope);
@ -1675,9 +1675,12 @@ IonBuilder::inspectOpcode(JSOp op)
return jsop_andor(op);
case JSOP_DEFVAR:
case JSOP_DEFCONST:
return jsop_defvar(GET_UINT32_INDEX(pc));
case JSOP_DEFLET:
case JSOP_DEFCONST:
return jsop_deflexical(GET_UINT32_INDEX(pc));
case JSOP_DEFFUN:
return jsop_deffun(GET_UINT32_INDEX(pc));
@ -1764,6 +1767,13 @@ IonBuilder::inspectOpcode(JSOp op)
current->setLocal(GET_LOCALNO(pc));
return true;
case JSOP_INITGLEXICAL: {
MDefinition* value = current->pop();
current->push(constant(ObjectValue(script()->global().lexicalScope())));
current->push(value);
return jsop_setprop(info().getAtom(pc)->asPropertyName());
}
case JSOP_CHECKALIASEDLEXICAL:
return jsop_checkaliasedlet(ScopeCoordinate(pc));
@ -1870,7 +1880,7 @@ IonBuilder::inspectOpcode(JSOp op)
PropertyName* name = info().getAtom(pc)->asPropertyName();
if (script()->hasNonSyntacticScope())
return jsop_setprop(name);
JSObject* obj = &script()->global();
JSObject* obj = testGlobalLexicalBinding(name);
return setStaticName(obj, name);
}
@ -1887,8 +1897,10 @@ IonBuilder::inspectOpcode(JSOp op)
}
case JSOP_BINDGNAME:
if (!script()->hasNonSyntacticScope())
return pushConstant(ObjectValue(script()->global()));
if (!script()->hasNonSyntacticScope()) {
JSObject* scope = testGlobalLexicalBinding(info().getName(pc));
return pushConstant(ObjectValue(*scope));
}
// Fall through to JSOP_BINDNAME
case JSOP_BINDNAME:
return jsop_bindname(info().getName(pc));
@ -7945,6 +7957,17 @@ NumFixedSlots(JSObject* object)
return gc::GetGCKindSlots(kind, object->getClass());
}
static bool
IsUninitializedGlobalLexicalSlot(JSObject* obj, PropertyName* name)
{
ClonedBlockObject &globalLexical = obj->as<ClonedBlockObject>();
MOZ_ASSERT(globalLexical.isGlobal());
Shape* shape = globalLexical.lookupPure(name);
if (!shape)
return false;
return globalLexical.getSlot(shape->slot()).isMagic(JS_UNINITIALIZED_LEXICAL);
}
bool
IonBuilder::getStaticName(JSObject* staticObject, PropertyName* name, bool* psucceeded,
MDefinition* lexicalCheck)
@ -7953,7 +7976,11 @@ IonBuilder::getStaticName(JSObject* staticObject, PropertyName* name, bool* psuc
jsid id = NameToId(name);
MOZ_ASSERT(staticObject->is<GlobalObject>() || staticObject->is<CallObject>());
bool isGlobalLexical = staticObject->is<ClonedBlockObject>() &&
staticObject->as<ClonedBlockObject>().isGlobal();
MOZ_ASSERT(isGlobalLexical ||
staticObject->is<GlobalObject>() ||
staticObject->is<CallObject>());
MOZ_ASSERT(staticObject->isSingleton());
*psucceeded = true;
@ -8000,6 +8027,13 @@ IonBuilder::getStaticName(JSObject* staticObject, PropertyName* name, bool* psuc
return true;
}
// Don't optimize global lexical bindings if they aren't initialized at
// compile time.
if (isGlobalLexical && IsUninitializedGlobalLexicalSlot(staticObject, name)) {
*psucceeded = false;
return true;
}
TemporaryTypeSet* types = bytecodeTypes(pc);
BarrierKind barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(), staticKey,
name, types, /* updateObserved = */ true);
@ -8055,7 +8089,11 @@ IonBuilder::setStaticName(JSObject* staticObject, PropertyName* name)
{
jsid id = NameToId(name);
MOZ_ASSERT(staticObject->is<GlobalObject>() || staticObject->is<CallObject>());
bool isGlobalLexical = staticObject->is<ClonedBlockObject>() &&
staticObject->as<ClonedBlockObject>().isGlobal();
MOZ_ASSERT(isGlobalLexical ||
staticObject->is<GlobalObject>() ||
staticObject->is<CallObject>());
MDefinition* value = current->peek(-1);
@ -8077,6 +8115,11 @@ IonBuilder::setStaticName(JSObject* staticObject, PropertyName* name)
if (!CanWriteProperty(alloc(), constraints(), property, value))
return jsop_setprop(name);
// Don't optimize global lexical bindings if they aren't initialized at
// compile time.
if (isGlobalLexical && IsUninitializedGlobalLexicalSlot(staticObject, name))
return jsop_setprop(name);
current->pop();
// Pop the bound object on the stack.
@ -8098,10 +8141,47 @@ IonBuilder::setStaticName(JSObject* staticObject, PropertyName* name)
value, needsBarrier, slotType);
}
JSObject*
IonBuilder::testGlobalLexicalBinding(PropertyName* name)
{
MOZ_ASSERT(JSOp(*pc) == JSOP_BINDGNAME ||
JSOp(*pc) == JSOP_GETGNAME ||
JSOp(*pc) == JSOP_SETGNAME ||
JSOp(*pc) == JSOP_STRICTSETGNAME);
// The global isn't the global lexical scope's prototype, but its
// enclosing scope. Test for the existence of |name| manually on the
// global lexical scope. If it is not found, look for it on the global
// itself.
NativeObject* obj = &script()->global().lexicalScope();
TypeSet::ObjectKey* lexicalKey = TypeSet::ObjectKey::get(obj);
jsid id = NameToId(name);
if (analysisContext)
lexicalKey->ensureTrackedProperty(analysisContext, id);
if (!lexicalKey->unknownProperties()) {
// If the property is not found on the global lexical scope but it is
// found on the global and is configurable, freeze the typeset for its
// non-existence.
//
// In the case that it is found on the global but is non-configurable,
// the binding cannot be shadowed by a global lexical binding.
HeapTypeSetKey lexicalProperty = lexicalKey->property(id);
if (!obj->containsPure(name)) {
Shape* shape = script()->global().lookupPure(name);
if (!shape || !shape->configurable())
MOZ_ALWAYS_FALSE(lexicalProperty.isOwnProperty(constraints()));
obj = &script()->global();
}
}
return obj;
}
bool
IonBuilder::jsop_getgname(PropertyName* name)
{
JSObject* obj = &script()->global();
JSObject* obj = testGlobalLexicalBinding(name);
bool emitted = false;
if (!getStaticName(obj, name, &emitted) || emitted)
return emitted;
@ -8121,7 +8201,7 @@ IonBuilder::jsop_getname(PropertyName* name)
{
MDefinition* object;
if (IsGlobalOp(JSOp(*pc)) && !script()->hasNonSyntacticScope()) {
MInstruction* global = constant(ObjectValue(script()->global()));
MInstruction* global = constant(ObjectValue(script()->global().lexicalScope()));
object = global;
} else {
current->push(current->scopeChain());
@ -12560,16 +12640,12 @@ IonBuilder::jsop_setarg(uint32_t arg)
bool
IonBuilder::jsop_defvar(uint32_t index)
{
MOZ_ASSERT(JSOp(*pc) == JSOP_DEFVAR || JSOp(*pc) == JSOP_DEFCONST);
MOZ_ASSERT(JSOp(*pc) == JSOP_DEFVAR);
PropertyName* name = script()->getName(index);
// Bake in attrs.
unsigned attrs = JSPROP_ENUMERATE;
if (JSOp(*pc) == JSOP_DEFCONST)
attrs |= JSPROP_READONLY;
else
attrs |= JSPROP_PERMANENT;
unsigned attrs = JSPROP_ENUMERATE | JSPROP_PERMANENT;
MOZ_ASSERT(!script()->isForEval());
// Pass the ScopeChain.
@ -12582,6 +12658,22 @@ IonBuilder::jsop_defvar(uint32_t index)
return resumeAfter(defvar);
}
bool
IonBuilder::jsop_deflexical(uint32_t index)
{
MOZ_ASSERT(JSOp(*pc) == JSOP_DEFLET || JSOp(*pc) == JSOP_DEFCONST);
PropertyName* name = script()->getName(index);
unsigned attrs = JSPROP_ENUMERATE | JSPROP_PERMANENT;
if (JSOp(*pc) == JSOP_DEFCONST)
attrs |= JSPROP_READONLY;
MDefLexical* deflex = MDefLexical::New(alloc(), name, attrs);
current->add(deflex);
return resumeAfter(deflex);
}
bool
IonBuilder::jsop_deffun(uint32_t index)
{

Просмотреть файл

@ -652,6 +652,7 @@ class IonBuilder
bool jsop_tostring();
bool jsop_setarg(uint32_t arg);
bool jsop_defvar(uint32_t index);
bool jsop_deflexical(uint32_t index);
bool jsop_deffun(uint32_t index);
bool jsop_notearg();
bool jsop_checklexical();
@ -961,6 +962,8 @@ class IonBuilder
MGetPropertyCache* getInlineableGetPropertyCache(CallInfo& callInfo);
JSObject* testGlobalLexicalBinding(PropertyName* name);
JSObject* testSingletonProperty(JSObject* obj, jsid id);
JSObject* testSingletonPropertyTypes(MDefinition* obj, jsid id);

Просмотреть файл

@ -1092,6 +1092,11 @@ GenerateCallGetter(JSContext* cx, IonScript* ion, MacroAssembler& masm,
Label pop1AndFail;
Label* maybePopAndFail = failures;
// If we're calling a getter on the global, inline the logic for the
// 'this' hook on the global lexical scope and manually push the global.
if (IsGlobalLexicalScope(obj))
masm.extractObject(Address(object, ScopeObject::offsetOfEnclosingScope()), object);
// Save off the object register if it aliases the scratchReg
if (spillObjReg) {
masm.push(object);
@ -3332,8 +3337,15 @@ SetPropertyIC::update(JSContext* cx, HandleScript outerScript, size_t cacheIndex
}
// Set/Add the property on the object, the inlined cache are setup for the next execution.
if (!SetProperty(cx, obj, name, value, cache.strict(), cache.pc()))
return false;
if (JSOp(*cache.pc()) == JSOP_INITGLEXICAL) {
RootedScript script(cx);
jsbytecode* pc;
cache.getScriptedLocation(&script, &pc);
InitGlobalLexicalOperation(cx, script, pc, value);
} else {
if (!SetProperty(cx, obj, name, value, cache.strict(), cache.pc()))
return false;
}
// A GC may have caused cache.value() to become stale as it is not traced.
// In this case the IonScript will have been invalidated, so check for that.

Просмотреть файл

@ -1000,6 +1000,12 @@ GetPcScript(JSContext* cx, JSScript** scriptRes, jsbytecode** pcRes);
CalleeToken
MarkCalleeToken(JSTracer* trc, CalleeToken token);
// The minimum stack size is two. Two slots are needed because INITGLEXICAL
// (stack depth 1) is compiled as a SETPROP (stack depth 2) on the global
// lexical scope. Baseline also requires one slot for this/argument type
// checks.
static const uint32_t MinJITStackSize = 2;
} /* namespace jit */
} /* namespace js */

Просмотреть файл

@ -153,6 +153,14 @@ LIRGenerator::visitDefVar(MDefVar* ins)
assignSafepoint(lir, ins);
}
void
LIRGenerator::visitDefLexical(MDefLexical* ins)
{
LDefLexical* lir = new(alloc()) LDefLexical();
add(lir, ins);
assignSafepoint(lir, ins);
}
void
LIRGenerator::visitDefFun(MDefFun* ins)
{

Просмотреть файл

@ -88,6 +88,7 @@ class LIRGenerator : public LIRGeneratorSpecific
void visitInitPropGetterSetter(MInitPropGetterSetter* ins);
void visitCheckOverRecursed(MCheckOverRecursed* ins);
void visitDefVar(MDefVar* ins);
void visitDefLexical(MDefLexical* ins);
void visitDefFun(MDefFun* ins);
void visitCreateThisWithTemplate(MCreateThisWithTemplate* ins);
void visitCreateThisWithProto(MCreateThisWithProto* ins);

Просмотреть файл

@ -7318,6 +7318,33 @@ class MDefVar
}
};
class MDefLexical
: public MNullaryInstruction
{
CompilerPropertyName name_; // Target name to be defined.
unsigned attrs_; // Attributes to be set.
private:
MDefLexical(PropertyName* name, unsigned attrs)
: name_(name),
attrs_(attrs)
{ }
public:
INSTRUCTION_HEADER(DefLexical)
static MDefLexical* New(TempAllocator& alloc, PropertyName* name, unsigned attrs) {
return new(alloc) MDefLexical(name, attrs);
}
PropertyName* name() const {
return name_;
}
unsigned attrs() const {
return attrs_;
}
};
class MDefFun
: public MUnaryInstruction,
public NoTypePolicy::Data

Просмотреть файл

@ -53,6 +53,7 @@ namespace jit {
_(UnarySharedStub) \
_(CheckOverRecursed) \
_(DefVar) \
_(DefLexical) \
_(DefFun) \
_(CreateThis) \
_(CreateThisWithProto) \

Просмотреть файл

@ -1228,6 +1228,16 @@ class LDefVar : public LCallInstructionHelper<0, 1, 0>
}
};
class LDefLexical : public LCallInstructionHelper<0, 0, 0>
{
public:
LIR_HEADER(DefLexical)
MDefLexical* mir() const {
return mir_->toDefLexical();
}
};
class LDefFun : public LCallInstructionHelper<0, 1, 0>
{
public:

Просмотреть файл

@ -68,6 +68,7 @@
_(InitPropGetterSetter) \
_(CheckOverRecursed) \
_(DefVar) \
_(DefLexical) \
_(DefFun) \
_(CallKnown) \
_(CallGeneric) \