mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 903394 - IonMonkey: Refactor IonBuilder jsop_setprop, r=jandem
This commit is contained in:
parent
d44d1fa6d6
commit
e82bab972a
@ -8251,149 +8251,269 @@ IonBuilder::jsop_setprop(HandlePropertyName name)
|
||||
MDefinition *value = current->pop();
|
||||
MDefinition *obj = current->pop();
|
||||
|
||||
types::StackTypeSet *objTypes = obj->resultTypeSet();
|
||||
RootedId id(cx, NameToId(name));
|
||||
bool emitted = false;
|
||||
|
||||
// Add post barrier if needed.
|
||||
if (NeedsPostBarrier(info(), value))
|
||||
current->add(MPostWriteBarrier::New(obj, value));
|
||||
|
||||
RootedId id(cx, NameToId(name));
|
||||
// Try to inline a common property setter, or make a call.
|
||||
if (!setPropTryCommonSetter(&emitted, obj, name, id, value) || emitted)
|
||||
return emitted;
|
||||
|
||||
bool barrier = PropertyWriteNeedsTypeBarrier(cx, current, &obj, name, &value);
|
||||
|
||||
// Try to emit store from definite slots.
|
||||
if (!setPropTryDefiniteSlot(&emitted, obj, name, value, barrier) || emitted)
|
||||
return emitted;
|
||||
|
||||
// Try to emit a monomorphic/polymorphic store based on baseline caches.
|
||||
if (!setPropTryInlineAccess(&emitted, obj, name, id, value, barrier) || emitted)
|
||||
return emitted;
|
||||
|
||||
// Try to emit a polymorphic cache.
|
||||
if (!setPropTryCache(&emitted, obj, name, value, barrier) || emitted)
|
||||
return emitted;
|
||||
|
||||
// Emit call.
|
||||
MInstruction *ins = MCallSetProperty::New(obj, value, name, script()->strict);
|
||||
current->add(ins);
|
||||
current->push(value);
|
||||
return resumeAfter(ins);
|
||||
}
|
||||
|
||||
bool
|
||||
IonBuilder::setPropTryCommonSetter(bool *emitted, MDefinition *obj,
|
||||
HandlePropertyName name, HandleId id,
|
||||
MDefinition *value)
|
||||
{
|
||||
JS_ASSERT(*emitted == false);
|
||||
|
||||
JSFunction *commonSetter;
|
||||
bool isDOM;
|
||||
|
||||
types::StackTypeSet *objTypes = obj->resultTypeSet();
|
||||
if (!TestCommonPropFunc(cx, objTypes, id, &commonSetter, false, &isDOM, NULL))
|
||||
return false;
|
||||
if (commonSetter) {
|
||||
// Setters can be called even if the property write needs a type
|
||||
// barrier, as calling the setter does not actually write any data
|
||||
// properties.
|
||||
RootedFunction setter(cx, commonSetter);
|
||||
if (isDOM && TestShouldDOMCall(cx, objTypes, setter, JSJitInfo::Setter)) {
|
||||
JS_ASSERT(setter->jitInfo()->type == JSJitInfo::Setter);
|
||||
MSetDOMProperty *set = MSetDOMProperty::New(setter->jitInfo()->setter, obj, value);
|
||||
if (!set)
|
||||
return false;
|
||||
|
||||
current->add(set);
|
||||
current->push(value);
|
||||
if (!commonSetter)
|
||||
return true;
|
||||
|
||||
return resumeAfter(set);
|
||||
}
|
||||
// Emit common setter.
|
||||
|
||||
// Don't call the setter with a primitive value.
|
||||
if (objTypes->getKnownTypeTag() != JSVAL_TYPE_OBJECT) {
|
||||
MGuardObject *guardObj = MGuardObject::New(obj);
|
||||
current->add(guardObj);
|
||||
obj = guardObj;
|
||||
}
|
||||
// Setters can be called even if the property write needs a type
|
||||
// barrier, as calling the setter does not actually write any data
|
||||
// properties.
|
||||
RootedFunction setter(cx, commonSetter);
|
||||
|
||||
// Dummy up the stack, as in getprop. We are pushing an extra value, so
|
||||
// ensure there is enough space.
|
||||
uint32_t depth = current->stackDepth() + 3;
|
||||
if (depth > current->nslots()) {
|
||||
if (!current->increaseSlots(depth - current->nslots()))
|
||||
return false;
|
||||
}
|
||||
// Try emitting dom call.
|
||||
if (!setPropTryCommonDOMSetter(emitted, obj, value, setter, isDOM) || emitted)
|
||||
return emitted;
|
||||
|
||||
pushConstant(ObjectValue(*setter));
|
||||
// Don't call the setter with a primitive value.
|
||||
if (objTypes->getKnownTypeTag() != JSVAL_TYPE_OBJECT) {
|
||||
MGuardObject *guardObj = MGuardObject::New(obj);
|
||||
current->add(guardObj);
|
||||
obj = guardObj;
|
||||
}
|
||||
|
||||
MPassArg *wrapper = MPassArg::New(obj);
|
||||
current->push(wrapper);
|
||||
current->add(wrapper);
|
||||
|
||||
MPassArg *arg = MPassArg::New(value);
|
||||
current->push(arg);
|
||||
current->add(arg);
|
||||
|
||||
// Call the setter. Note that we have to push the original value, not
|
||||
// the setter's return value.
|
||||
CallInfo callInfo(cx, false);
|
||||
if (!callInfo.init(current, 1))
|
||||
// Dummy up the stack, as in getprop. We are pushing an extra value, so
|
||||
// ensure there is enough space.
|
||||
uint32_t depth = current->stackDepth() + 3;
|
||||
if (depth > current->nslots()) {
|
||||
if (!current->increaseSlots(depth - current->nslots()))
|
||||
return false;
|
||||
// Ensure that we know we are calling a setter in case we inline it.
|
||||
callInfo.markAsSetter();
|
||||
}
|
||||
|
||||
// Inline the setter if we can.
|
||||
if (makeInliningDecision(setter, callInfo) && setter->isInterpreted())
|
||||
return inlineScriptedCall(callInfo, setter);
|
||||
pushConstant(ObjectValue(*setter));
|
||||
|
||||
MCall *call = makeCallHelper(setter, callInfo, false);
|
||||
if (!call)
|
||||
MPassArg *wrapper = MPassArg::New(obj);
|
||||
current->push(wrapper);
|
||||
current->add(wrapper);
|
||||
|
||||
MPassArg *arg = MPassArg::New(value);
|
||||
current->push(arg);
|
||||
current->add(arg);
|
||||
|
||||
// Call the setter. Note that we have to push the original value, not
|
||||
// the setter's return value.
|
||||
CallInfo callInfo(cx, false);
|
||||
if (!callInfo.init(current, 1))
|
||||
return false;
|
||||
|
||||
// Ensure that we know we are calling a setter in case we inline it.
|
||||
callInfo.markAsSetter();
|
||||
|
||||
// Inline the setter if we can.
|
||||
if (makeInliningDecision(setter, callInfo) && setter->isInterpreted()) {
|
||||
if (!inlineScriptedCall(callInfo, setter))
|
||||
return false;
|
||||
|
||||
current->push(value);
|
||||
return resumeAfter(call);
|
||||
*emitted = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (PropertyWriteNeedsTypeBarrier(cx, current, &obj, name, &value)) {
|
||||
MInstruction *ins = MCallSetProperty::New(obj, value, name, script()->strict);
|
||||
current->add(ins);
|
||||
current->push(value);
|
||||
return resumeAfter(ins);
|
||||
}
|
||||
MCall *call = makeCallHelper(setter, callInfo, false);
|
||||
if (!call)
|
||||
return false;
|
||||
|
||||
if (types::HeapTypeSet *propTypes = GetDefiniteSlot(cx, objTypes, name)) {
|
||||
MStoreFixedSlot *fixed = MStoreFixedSlot::New(obj, propTypes->definiteSlot(), value);
|
||||
current->add(fixed);
|
||||
current->push(value);
|
||||
if (propTypes->needsBarrier(cx))
|
||||
fixed->setNeedsBarrier();
|
||||
return resumeAfter(fixed);
|
||||
}
|
||||
current->push(value);
|
||||
if (!resumeAfter(call))
|
||||
return false;
|
||||
|
||||
*emitted = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
IonBuilder::setPropTryCommonDOMSetter(bool *emitted, MDefinition *obj,
|
||||
MDefinition *value, HandleFunction setter,
|
||||
bool isDOM)
|
||||
{
|
||||
JS_ASSERT(*emitted == false);
|
||||
|
||||
if (!isDOM)
|
||||
return true;
|
||||
|
||||
types::StackTypeSet *objTypes = obj->resultTypeSet();
|
||||
if (!TestShouldDOMCall(cx, objTypes, setter, JSJitInfo::Setter))
|
||||
return true;
|
||||
|
||||
// Emit SetDOMProperty.
|
||||
JS_ASSERT(setter->jitInfo()->type == JSJitInfo::Setter);
|
||||
MSetDOMProperty *set = MSetDOMProperty::New(setter->jitInfo()->setter, obj, value);
|
||||
|
||||
current->add(set);
|
||||
current->push(value);
|
||||
|
||||
if (!resumeAfter(set))
|
||||
return false;
|
||||
|
||||
*emitted = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
IonBuilder::setPropTryDefiniteSlot(bool *emitted, MDefinition *obj,
|
||||
HandlePropertyName name, MDefinition *value,
|
||||
bool barrier)
|
||||
{
|
||||
JS_ASSERT(*emitted == false);
|
||||
|
||||
if (barrier)
|
||||
return true;
|
||||
|
||||
types::StackTypeSet *objTypes = obj->resultTypeSet();
|
||||
types::HeapTypeSet *propTypes = GetDefiniteSlot(cx, objTypes, name);
|
||||
if (!propTypes)
|
||||
return true;
|
||||
|
||||
MStoreFixedSlot *fixed = MStoreFixedSlot::New(obj, propTypes->definiteSlot(), value);
|
||||
current->add(fixed);
|
||||
current->push(value);
|
||||
|
||||
if (propTypes->needsBarrier(cx))
|
||||
fixed->setNeedsBarrier();
|
||||
|
||||
if (!resumeAfter(fixed))
|
||||
return false;
|
||||
|
||||
*emitted = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
IonBuilder::setPropTryInlineAccess(bool *emitted, MDefinition *obj,
|
||||
HandlePropertyName name, HandleId id,
|
||||
MDefinition *value, bool barrier)
|
||||
{
|
||||
JS_ASSERT(*emitted == false);
|
||||
|
||||
if (barrier)
|
||||
return true;
|
||||
|
||||
Vector<Shape *> shapes(cx);
|
||||
if (!inspector->maybeShapesForPropertyOp(pc, shapes))
|
||||
return false;
|
||||
|
||||
if (!shapes.empty() && CanInlinePropertyOpShapes(shapes)) {
|
||||
if (shapes.length() == 1) {
|
||||
spew("Inlining monomorphic SETPROP");
|
||||
if (shapes.empty())
|
||||
return true;
|
||||
|
||||
// The JM IC was monomorphic, so we inline the property access as
|
||||
// long as the shape is not in dictionary mode. We cannot be sure
|
||||
// that the shape is still a lastProperty, and calling Shape::search
|
||||
// on dictionary mode shapes that aren't lastProperty is invalid.
|
||||
Shape *objShape = shapes[0];
|
||||
obj = addShapeGuard(obj, objShape, Bailout_CachedShapeGuard);
|
||||
if (!CanInlinePropertyOpShapes(shapes))
|
||||
return true;
|
||||
|
||||
Shape *shape = objShape->search(cx, NameToId(name));
|
||||
types::StackTypeSet *objTypes = obj->resultTypeSet();
|
||||
|
||||
if (shapes.length() == 1) {
|
||||
spew("Inlining monomorphic SETPROP");
|
||||
|
||||
// The Baseline IC was monomorphic, so we inline the property access as
|
||||
// long as the shape is not in dictionary mode. We cannot be sure
|
||||
// that the shape is still a lastProperty, and calling Shape::search
|
||||
// on dictionary mode shapes that aren't lastProperty is invalid.
|
||||
Shape *objShape = shapes[0];
|
||||
obj = addShapeGuard(obj, objShape, Bailout_CachedShapeGuard);
|
||||
|
||||
Shape *shape = objShape->search(cx, NameToId(name));
|
||||
JS_ASSERT(shape);
|
||||
|
||||
bool needsBarrier = objTypes->propertyNeedsBarrier(cx, id);
|
||||
if (!storeSlot(obj, shape, value, needsBarrier))
|
||||
return false;
|
||||
} else {
|
||||
JS_ASSERT(shapes.length() > 1);
|
||||
spew("Inlining polymorphic SETPROP");
|
||||
|
||||
MSetPropertyPolymorphic *ins = MSetPropertyPolymorphic::New(obj, value);
|
||||
current->add(ins);
|
||||
current->push(value);
|
||||
|
||||
for (size_t i = 0; i < shapes.length(); i++) {
|
||||
Shape *objShape = shapes[i];
|
||||
Shape *shape = objShape->search(cx, id);
|
||||
JS_ASSERT(shape);
|
||||
|
||||
bool needsBarrier = objTypes->propertyNeedsBarrier(cx, id);
|
||||
return storeSlot(obj, shape, value, needsBarrier);
|
||||
} else {
|
||||
JS_ASSERT(shapes.length() > 1);
|
||||
spew("Inlining polymorphic SETPROP");
|
||||
|
||||
MSetPropertyPolymorphic *ins = MSetPropertyPolymorphic::New(obj, value);
|
||||
current->add(ins);
|
||||
current->push(value);
|
||||
|
||||
for (size_t i = 0; i < shapes.length(); i++) {
|
||||
Shape *objShape = shapes[i];
|
||||
Shape *shape = objShape->search(cx, id);
|
||||
JS_ASSERT(shape);
|
||||
if (!ins->addShape(objShape, shape))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (objTypes->propertyNeedsBarrier(cx, id))
|
||||
ins->setNeedsBarrier();
|
||||
|
||||
return resumeAfter(ins);
|
||||
if (!ins->addShape(objShape, shape))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (objTypes->propertyNeedsBarrier(cx, id))
|
||||
ins->setNeedsBarrier();
|
||||
|
||||
if (!resumeAfter(ins))
|
||||
return false;
|
||||
}
|
||||
|
||||
spew("SETPROP IC");
|
||||
*emitted = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
IonBuilder::setPropTryCache(bool *emitted, MDefinition *obj,
|
||||
HandlePropertyName name, MDefinition *value,
|
||||
bool barrier)
|
||||
{
|
||||
JS_ASSERT(*emitted == false);
|
||||
|
||||
if (barrier)
|
||||
return true;
|
||||
|
||||
// Emit SetPropertyCache.
|
||||
MSetPropertyCache *ins = MSetPropertyCache::New(obj, value, name, script()->strict);
|
||||
|
||||
RootedId id(cx, NameToId(name));
|
||||
types::StackTypeSet *objTypes = obj->resultTypeSet();
|
||||
if (!objTypes || objTypes->propertyNeedsBarrier(cx, id))
|
||||
ins->setNeedsBarrier();
|
||||
|
||||
current->add(ins);
|
||||
current->push(value);
|
||||
|
||||
return resumeAfter(ins);
|
||||
if (!resumeAfter(ins))
|
||||
return false;
|
||||
|
||||
*emitted = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -367,6 +367,23 @@ class IonBuilder : public MIRGenerator
|
||||
bool getPropTryCache(bool *emitted, HandlePropertyName name, HandleId id,
|
||||
bool barrier, types::StackTypeSet *types);
|
||||
|
||||
// jsop_setprop() helpers.
|
||||
bool setPropTryCommonSetter(bool *emitted, MDefinition *obj,
|
||||
HandlePropertyName name, HandleId id,
|
||||
MDefinition *value);
|
||||
bool setPropTryCommonDOMSetter(bool *emitted, MDefinition *obj,
|
||||
MDefinition *value, HandleFunction setter,
|
||||
bool isDOM);
|
||||
bool setPropTryDefiniteSlot(bool *emitted, MDefinition *obj,
|
||||
HandlePropertyName name, MDefinition *value,
|
||||
bool barrier);
|
||||
bool setPropTryInlineAccess(bool *emitted, MDefinition *obj,
|
||||
HandlePropertyName name, HandleId id,
|
||||
MDefinition *value, bool barrier);
|
||||
bool setPropTryCache(bool *emitted, MDefinition *obj,
|
||||
HandlePropertyName name, MDefinition *value,
|
||||
bool barrier);
|
||||
|
||||
// jsop_setelem() helpers.
|
||||
bool setElemTryTyped(bool *emitted, MDefinition *object,
|
||||
MDefinition *index, MDefinition *value);
|
||||
|
Loading…
Reference in New Issue
Block a user