Bug 747288 - Implemented inlined calls to specialized DOM accessors. (r=sstangl)
authorEric Faust <efaust@mozilla.com>
Wed, 08 Aug 2012 17:02:46 -0700
changeset 103689 30894762f1fcdc36cfd64ccd1ce2c8473a8ebf4b
parent 103688 0400c12c5295a0ed6b720332496eec9c4a757a72
child 103690 b2382c3c24cedc2fcd27d07a7a53ffc1fd0bdb3f
push id1199
push userefaust@mozilla.com
push dateThu, 09 Aug 2012 00:02:55 +0000
reviewerssstangl
bugs747288
milestone17.0a1
Bug 747288 - Implemented inlined calls to specialized DOM accessors. (r=sstangl)
js/src/ion/CodeGenerator.cpp
js/src/ion/CodeGenerator.h
js/src/ion/IonBuilder.cpp
js/src/ion/IonBuilder.h
js/src/ion/IonFrameIterator.h
js/src/ion/IonFrames.cpp
js/src/ion/IonMacroAssembler.h
js/src/ion/LIR-Common.h
js/src/ion/LOpcodes.h
js/src/ion/Lowering.cpp
js/src/ion/Lowering.h
js/src/ion/MIR.h
js/src/ion/MOpcodes.h
js/src/ion/arm/IonFrames-arm.h
js/src/ion/shared/CodeGenerator-shared-inl.h
js/src/ion/shared/IonFrames-shared.h
js/src/ion/shared/IonFrames-x86-shared.h
--- a/js/src/ion/CodeGenerator.cpp
+++ b/js/src/ion/CodeGenerator.cpp
@@ -66,17 +66,17 @@ CodeGenerator::visitValueToInt32(LValueT
         // Test for string or object - then fallthrough to null, which will
         // also handle undefined.
         masm.branchTestObject(Assembler::Equal, operand, &fails);
         masm.branchTestString(Assembler::Equal, operand, &fails);
     }
 
     if (fails.used() && !bailoutFrom(&fails, lir->snapshot()))
         return false;
-    
+
     // The value is null - just emit 0.
     masm.mov(Imm32(0), output);
     masm.jump(&done);
 
     // Just unbox a bool, the result is 0 or 1.
     masm.bind(&isBool);
     masm.unboxBoolean(operand, output);
     masm.jump(&done);
@@ -3643,25 +3643,25 @@ CodeGenerator::visitInstanceOfV(LInstanc
     return emitInstanceOf(ins, rhs);
 }
 
 bool
 CodeGenerator::emitInstanceOf(LInstruction *ins, Register rhs)
 {
     Register rhsTmp = ToRegister(ins->getTemp(1));
     Register output = ToRegister(ins->getDef(0));
-    
-    // This temporary is used in other parts of the code. 
+
+    // This temporary is used in other parts of the code.
     // Different names are used so the purpose is clear.
     Register rhsFlags = ToRegister(ins->getTemp(0));
     Register lhsTmp = ToRegister(ins->getTemp(0));
 
-    Label callHasInstance;    
-    Label boundFunctionCheck;    
-    Label boundFunctionDone;    
+    Label callHasInstance;
+    Label boundFunctionCheck;
+    Label boundFunctionDone;
     Label done;
     Label loopPrototypeChain;
 
     typedef bool (*pf)(JSContext *, HandleObject, const Value &, JSBool *);
     static const VMFunction HasInstanceInfo = FunctionInfo<pf>(js::HasInstance);
 
     OutOfLineCode *call = oolCallVM(HasInstanceInfo, ins, (ArgList(), rhs, ToValue(ins, 0)),
                                    StoreRegisterTo(output));
@@ -3690,56 +3690,56 @@ CodeGenerator::emitInstanceOf(LInstructi
 
     // Check Bound Function
     masm.loadPtr(Address(output, BaseShape::offsetOfFlags()), rhsFlags);
     masm.and32(Imm32(BaseShape::BOUND_FUNCTION), rhsFlags);
     masm.j(Assembler::Zero, &boundFunctionDone);
 
     // Get Bound Function
     masm.loadPtr(Address(output, BaseShape::offsetOfParent()), rhsTmp);
-    masm.jump(&boundFunctionCheck); 
+    masm.jump(&boundFunctionCheck);
 
     // 2. CODE FOR HASINSTANCE_FUNCTION
-    masm.bind(&boundFunctionDone); 
+    masm.bind(&boundFunctionDone);
 
     // ASM-equivalent of following code
     //  if (!lhs->isObject()) {
     //    output = false;
     //    goto done;
     //  }
     //  rhs = rhs->getPrototypeClass();
     //  output = false;
     //  while (1) {
     //    lhs = lhs->getType().proto;
     //    if (lhs == NULL)
     //      goto done;
     //    if (lhs != rhs) {
     //      output = true;
-    //      goto done; 
+    //      goto done;
     //    }
     //  }
 
     // When lhs is a value: The HasInstance for function objects always
-    // return false when lhs isn't an object. So check if 
+    // return false when lhs isn't an object. So check if
     // lhs is an object and otherwise return false
     if (ins->isInstanceOfV()) {
         Label isObject;
         ValueOperand lhsValue = ToValue(ins, LInstanceOfV::LHS);
         masm.branchTestObject(Assembler::Equal, lhsValue, &isObject);
         masm.mov(Imm32(0), output);
-        masm.jump(&done); 
+        masm.jump(&done);
 
         masm.bind(&isObject);
         Register tmp = masm.extractObject(lhsValue, lhsTmp);
         masm.mov(tmp, lhsTmp);
     } else {
         masm.mov(ToRegister(ins->getOperand(0)), lhsTmp);
     }
 
-    // Get prototype-class by using a OutOfLine GetProperty Cache    
+    // Get prototype-class by using a OutOfLine GetProperty Cache
     // It will use register 'rhsTmp' as input and register 'output' as output, see r1889
     OutOfLineCache *ool = new OutOfLineCache(ins);
     if (!addOutOfLineCode(ool))
         return false;
 
     CodeOffsetJump jump = masm.jumpWithPatch(ool->repatchEntry());
     CodeOffsetLabel label = masm.labelForPatch();
     masm.bind(ool->rejoin());
@@ -3757,24 +3757,148 @@ CodeGenerator::emitInstanceOf(LInstructi
     masm.test32(lhsTmp, lhsTmp);
     masm.j(Assembler::Zero, &done);
 
     // Check lhs is equal to rhsShape
     masm.cmp32(lhsTmp, rhsTmp);
     masm.j(Assembler::NotEqual, &loopPrototypeChain);
 
     // return true
-    masm.mov(Imm32(1), output); 
+    masm.mov(Imm32(1), output);
 
     masm.bind(call->rejoin());
     masm.bind(&done);
     return true;
 }
 
 bool
+CodeGenerator::visitGetDOMProperty(LGetDOMProperty *ins)
+{
+    const Register JSContextReg = ToRegister(ins->getJSContextReg());
+    const Register ObjectReg = ToRegister(ins->getObjectReg());
+    const Register PrivateReg = ToRegister(ins->getPrivReg());
+    const Register ValueReg = ToRegister(ins->getValueReg());
+
+    DebugOnly<uint32> initialStack = masm.framePushed();
+
+    masm.checkStackAlignment();
+
+    /* Make Space for the outparam */
+    masm.adjustStack((int)-sizeof(Value));
+    masm.movePtr(StackPointer, ValueReg);
+
+    masm.Push(ObjectReg);
+
+    // GetReservedSlot(obj, DOM_PROTO_INSTANCE_CLASS_SLOT).toPrivate()
+    masm.loadPrivate(Address(ObjectReg, JSObject::getFixedSlotOffset(0)), PrivateReg);
+
+    // Rooting will happen at GC time.
+    masm.movePtr(StackPointer, ObjectReg);
+
+    uint32 safepointOffset;
+    if (!masm.buildFakeExitFrame(JSContextReg, &safepointOffset))
+        return false;
+    masm.enterFakeDOMFrame(ION_FRAME_DOMGETTER);
+
+    if (!markSafepointAt(safepointOffset, ins))
+        return false;
+
+    masm.setupUnalignedABICall(4, JSContextReg);
+
+    masm.loadJSContext(JSContextReg);
+
+    masm.passABIArg(JSContextReg);
+    masm.passABIArg(ObjectReg);
+    masm.passABIArg(PrivateReg);
+    masm.passABIArg(ValueReg);
+    masm.callWithABI((void *)ins->mir()->fun());
+
+    if (ins->mir()->isInfallible()) {
+        masm.loadValue(Address(StackPointer, IonDOMExitFrameLayout::offsetOfResult()),
+                       JSReturnOperand);
+    } else {
+        Label success, exception;
+        masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, &exception);
+
+        masm.loadValue(Address(StackPointer, IonDOMExitFrameLayout::offsetOfResult()),
+                    JSReturnOperand);
+        masm.jump(&success);
+
+        {
+            masm.bind(&exception);
+            masm.handleException();
+        }
+        masm.bind(&success);
+    }
+    masm.adjustStack(IonDOMExitFrameLayout::Size());
+
+    JS_ASSERT(masm.framePushed() == initialStack);
+    return true;
+}
+
+bool
+CodeGenerator::visitSetDOMProperty(LSetDOMProperty *ins)
+{
+    const Register JSContextReg = ToRegister(ins->getJSContextReg());
+    const Register ObjectReg = ToRegister(ins->getObjectReg());
+    const Register PrivateReg = ToRegister(ins->getPrivReg());
+    const Register ValueReg = ToRegister(ins->getValueReg());
+
+    DebugOnly<uint32> initialStack = masm.framePushed();
+
+    masm.checkStackAlignment();
+
+    // Push thei argument. Rooting will happen at GC time.
+    ValueOperand argVal = ToValue(ins, LSetDOMProperty::Value);
+    masm.Push(argVal);
+    masm.movePtr(StackPointer, ValueReg);
+
+    masm.Push(ObjectReg);
+
+    // GetReservedSlot(obj, DOM_PROTO_INSTANCE_CLASS_SLOT).toPrivate()
+    masm.loadPrivate(Address(ObjectReg, JSObject::getFixedSlotOffset(0)), PrivateReg);
+
+    // Rooting will happen at GC time.
+    masm.movePtr(StackPointer, ObjectReg);
+
+    uint32 safepointOffset;
+    if (!masm.buildFakeExitFrame(JSContextReg, &safepointOffset))
+        return false;
+    masm.enterFakeDOMFrame(ION_FRAME_DOMSETTER);
+
+    if (!markSafepointAt(safepointOffset, ins))
+        return false;
+
+    masm.setupUnalignedABICall(4, JSContextReg);
+
+    masm.loadJSContext(JSContextReg);
+
+    masm.passABIArg(JSContextReg);
+    masm.passABIArg(ObjectReg);
+    masm.passABIArg(PrivateReg);
+    masm.passABIArg(ValueReg);
+    masm.callWithABI((void *)ins->mir()->fun());
+
+    Label success, exception;
+    masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, &exception);
+
+    masm.jump(&success);
+
+    {
+        masm.bind(&exception);
+        masm.handleException();
+    }
+    masm.bind(&success);
+    masm.adjustStack(IonDOMExitFrameLayout::Size());
+
+    JS_ASSERT(masm.framePushed() == initialStack);
+    return true;
+}
+
+bool
 CodeGenerator::visitProfilingEnter(LProfilingEnter *lir)
 {
     SPSProfiler *profiler = &gen->compartment->rt->spsProfiler;
     JS_ASSERT(profiler->enabled());
 
     const char *string = lir->profileString();
 
     Register size = ToRegister(lir->temp1()->output());
--- a/js/src/ion/CodeGenerator.h
+++ b/js/src/ion/CodeGenerator.h
@@ -157,16 +157,18 @@ class CodeGenerator : public CodeGenerat
     bool visitCallDeleteProperty(LCallDeleteProperty *lir);
     bool visitBitNotV(LBitNotV *lir);
     bool visitBitOpV(LBitOpV *lir);
     bool emitInstanceOf(LInstruction *ins, Register rhs);
     bool visitInstanceOfO(LInstanceOfO *ins);
     bool visitInstanceOfV(LInstanceOfV *ins);
     bool visitProfilingEnter(LProfilingEnter *lir);
     bool visitProfilingExit(LProfilingExit *lir);
+    bool visitGetDOMProperty(LGetDOMProperty *lir);
+    bool visitSetDOMProperty(LSetDOMProperty *lir);
 
     bool visitCheckOverRecursed(LCheckOverRecursed *lir);
     bool visitCheckOverRecursedFailure(CheckOverRecursedFailure *ool);
 
     bool visitUnboxDouble(LUnboxDouble *lir);
     bool visitOutOfLineUnboxDouble(OutOfLineUnboxDouble *ool);
     bool visitOutOfLineStoreElementHole(OutOfLineStoreElementHole *ool);
 
--- a/js/src/ion/IonBuilder.cpp
+++ b/js/src/ion/IonBuilder.cpp
@@ -5211,22 +5211,25 @@ IonBuilder::jsop_not()
     current->add(ins);
     current->push(ins);
     return true;
 }
 
 
 inline bool
 IonBuilder::TestCommonPropFunc(JSContext *cx, types::TypeSet *types, HandleId id,
-                   JSFunction **funcp, bool isGetter)
+                               JSFunction **funcp, bool isGetter, bool *isDOM)
 {
     JSObject *found = NULL;
     JSObject *foundProto = NULL;
 
     *funcp = NULL;
+    *isDOM = false;
+
+    bool thinkDOM = true;
 
     // No sense looking if we don't know what's going on.
     if (!types || types->unknownObject())
         return true;
 
     // Iterate down all the types to see if they all have the same getter or
     // setter.
     for (unsigned i = 0; i < types->getObjectCount(); i++) {
@@ -5246,22 +5249,29 @@ IonBuilder::TestCommonPropFunc(JSContext
             // the chain.
             jsid typeId = types::MakeTypeId(cx, id);
             types::TypeSet *propSet = typeObj->getProperty(cx, typeId, false);
             if (!propSet)
                 return false;
             if (propSet->isOwnProperty(false))
                 return true;
 
+            // Check the DOM status of the instance type
+            thinkDOM = thinkDOM && !typeObj->hasAnyFlags(types::OBJECT_FLAG_NON_DOM);
+
             // Otherwise try using the prototype.
             curObj = typeObj->proto;
         } else {
             // Can't optimize setters on watched singleton objects.
             if (!isGetter && curObj->watched())
                 return true;
+
+            // Check the DOM-ness of the singleton.
+            types::TypeObject *objType = curObj->getType(cx);
+            thinkDOM = thinkDOM && !objType->hasAnyFlags(types::OBJECT_FLAG_NON_DOM);
         }
 
         // Turns out that we need to check for a property lookup op, else we
         // will end up calling it mid-compilation.
         if (!CanEffectlesslyCallLookupGenericOnObject(curObj))
             return true;
 
         RootedObject proto(cx);
@@ -5350,39 +5360,82 @@ IonBuilder::TestCommonPropFunc(JSContext
         if (!curType) {
             obj = types->getSingleObject(i);
             if (!obj)
                 continue;
 
             curType = obj->getType(cx);
         }
 
+        // Freeze the types as being DOM objects if they are
+        if (thinkDOM) {
+            // Asking the question adds the freeze
+            DebugOnly<bool> wasntDOM = types::TypeSet::HasObjectFlags(cx, curType,
+                                                           types::OBJECT_FLAG_NON_DOM);
+            JS_ASSERT(!wasntDOM);
+        }
+
         // If we found a Singleton object's own-property, there's nothing to
         // freeze.
         if (obj != foundProto) {
             // Walk the prototype chain. Everyone has to have the property, since we
             // just checked, so propSet cannot be NULL.
             jsid typeId = types::MakeTypeId(cx, id);
             while (true) {
                 types::TypeSet *propSet = curType->getProperty(cx, typeId, false);
                 JS_ASSERT(propSet);
-                // Asking the question adds the freeze
-                bool isOwn = propSet->isOwnProperty(cx, curType, false);
+                // Asking, freeze by asking.
+                DebugOnly<bool> isOwn = propSet->isOwnProperty(cx, curType, false);
                 JS_ASSERT(!isOwn);
                 // Don't mark the proto. It will be held down by the shape
                 // guard. This allows us tp use properties found on prototypes
                 // with properties unknown to TI.
                 if (curType->proto == foundProto)
                     break;
                 curType = curType->proto->getType(cx);
             }
         }
     }
 
     *funcp = found->toFunction();
+    *isDOM = thinkDOM;
+
+    return true;
+}
+
+static bool
+TestShouldDOMCall(JSContext *cx, types::TypeSet *inTypes, HandleFunction func)
+{
+    if (!func->isNative() || !func->jitInfo())
+        return false;
+    // If all the DOM objects flowing through are legal with this
+    // property, we can bake in a call to the bottom half of the DOM
+    // accessor
+    DOMInstanceClassMatchesProto instanceChecker =
+        GetDOMCallbacks(cx->runtime)->instanceClassMatchesProto;
+
+    const JSJitInfo *jinfo = func->jitInfo();
+
+    for (unsigned i = 0; i < inTypes->getObjectCount(); i++) {
+        types::TypeObject *curType = inTypes->getTypeObject(i);
+
+        if (!curType) {
+            JSObject *curObj = inTypes->getSingleObject(i);
+
+            if (!curObj)
+                continue;
+
+            curType = curObj->getType(cx);
+        }
+
+        JSObject *typeProto = curType->proto;
+        RootedObject proto(cx, typeProto);
+        if (!instanceChecker(proto, jinfo->protoID, jinfo->depth))
+            return false;
+    }
 
     return true;
 }
 
 bool
 IonBuilder::annotateGetPropertyCache(JSContext *cx, MDefinition *obj, MGetPropertyCache *getPropCache,
                                     types::TypeSet *objTypes, types::TypeSet *pushedTypes)
 {
@@ -5606,28 +5659,40 @@ IonBuilder::jsop_getprop(HandlePropertyN
         current->add(fixed);
         current->push(fixed);
 
         return pushTypeBarrier(fixed, types, barrier);
     }
 
     // Attempt to inline common property getter. At least patch to call instead.
     JSFunction *commonGetter;
-    if (!TestCommonPropFunc(cx, unaryTypes.inTypes, id, &commonGetter, true))
-        return false;
-    if (commonGetter) {
+    bool isDOM;
+    if (!TestCommonPropFunc(cx, unaryTypes.inTypes, id, &commonGetter, true, &isDOM))
+         return false;
+     if (commonGetter) {
+        RootedFunction getter(cx, commonGetter);
+        if (isDOM && TestShouldDOMCall(cx, unaryTypes.inTypes, getter)) {
+            const JSJitInfo *jitinfo = getter->jitInfo();
+            MGetDOMProperty *get = MGetDOMProperty::New(jitinfo->op, obj, jitinfo->isInfallible);
+
+            current->add(get);
+            current->push(get);
+
+            if (!resumeAfter(get))
+                return false;
+
+            return pushTypeBarrier(get, types, barrier);
+        }
         // Spoof stack to expected state for call.
         pushConstant(ObjectValue(*commonGetter));
 
         MPassArg *wrapper = MPassArg::New(obj);
         current->push(wrapper);
         current->add(wrapper);
 
-        RootedFunction getter(cx, commonGetter);
-
         return makeCallBarrier(getter, 0, false, types, barrier);
     }
 
     if (unary.ival == MIRType_Object) {
         MIRType rvalType = MIRType_Value;
         if (!barrier && !IsNullOrUndefined(unary.rval))
             rvalType = unary.rval;
 
@@ -5696,34 +5761,46 @@ IonBuilder::jsop_setprop(HandlePropertyN
             current->push(value);
             if (propTypes->needsBarrier(cx))
                 fixed->setNeedsBarrier();
             return resumeAfter(fixed);
         }
     }
 
     RootedId id(cx, NameToId(name));
+    types::TypeSet *types = binaryTypes.lhsTypes;
 
     JSFunction *commonSetter;
-    if (!TestCommonPropFunc(cx, binaryTypes.lhsTypes, id, &commonSetter, false))
+    bool isDOM;
+    if (!TestCommonPropFunc(cx, types, id, &commonSetter, false, &isDOM))
         return false;
     if (!monitored && commonSetter) {
+        RootedFunction setter(cx, commonSetter);
+        if (isDOM && TestShouldDOMCall(cx, types, setter)) {
+            MSetDOMProperty *set = MSetDOMProperty::New(setter->jitInfo()->op, obj, value);
+            if (!set)
+                return false;
+
+            current->add(set);
+            current->push(value);
+
+            return resumeAfter(set);
+        }
+
         // Dummy up the stack, as in getprop
-        pushConstant(ObjectValue(*commonSetter));
+        pushConstant(ObjectValue(*setter));
 
         MPassArg *wrapper = MPassArg::New(obj);
         current->push(wrapper);
         current->add(wrapper);
 
         MPassArg *arg = MPassArg::New(value);
         current->push(arg);
         current->add(arg);
 
-        RootedFunction setter(cx, commonSetter);
-
         return makeCallBarrier(setter, 1, false, NULL, NULL);
     }
 
     oracle->binaryOp(script, pc);
 
     MSetPropertyInstruction *ins;
     if (monitored) {
         ins = MCallSetProperty::New(obj, value, name, script->strictModeCode);
--- a/js/src/ion/IonBuilder.h
+++ b/js/src/ion/IonBuilder.h
@@ -395,26 +395,26 @@ class IonBuilder : public MIRGenerator
     bool jsop_call_inline(HandleFunction callee, uint32 argc, bool constructing,
                           MConstant *constFun, MBasicBlock *bottom,
                           Vector<MDefinition *, 8, IonAllocPolicy> &retvalDefns);
     bool inlineScriptedCall(AutoObjectVector &targets, uint32 argc, bool constructing,
                             types::TypeSet *types, types::TypeSet *barrier);
     bool makeInliningDecision(AutoObjectVector &targets);
 
     bool jsop_call_fun_barrier(AutoObjectVector &targets, uint32_t numTargets,
-                               uint32 argc, 
+                               uint32 argc,
                                bool constructing,
                                types::TypeSet *types,
                                types::TypeSet *barrier);
     bool makeCallBarrier(HandleFunction target, uint32 argc, bool constructing,
                          types::TypeSet *types, types::TypeSet *barrier);
 
     inline bool TestCommonPropFunc(JSContext *cx, types::TypeSet *types,
-                                   HandleId id, JSFunction **funcp, 
-                                   bool isGetter);
+                                   HandleId id, JSFunction **funcp,
+                                   bool isGetter, bool *isDOM);
 
     bool annotateGetPropertyCache(JSContext *cx, MDefinition *obj, MGetPropertyCache *getPropCache,
                                   types::TypeSet *objTypes, types::TypeSet *pushedTypes);
 
     MGetPropertyCache *checkInlineableGetPropertyCache(uint32_t argc);
 
     MPolyInlineDispatch *
     makePolyInlineDispatch(JSContext *cx, AutoObjectVector &targets, int argc,
--- a/js/src/ion/IonFrameIterator.h
+++ b/js/src/ion/IonFrameIterator.h
@@ -108,16 +108,17 @@ class IonFrameIterator
     // places the invalidated Ion script in |ionScript|.
     bool checkInvalidation(IonScript **ionScript) const;
     bool checkInvalidation() const;
 
     bool isScripted() const {
         return type_ == IonFrame_JS;
     }
     bool isNative() const;
+    bool isDOMExit() const;
     bool isEntry() const {
         return type_ == IonFrame_Entry;
     }
     bool isFunctionFrame() const;
 
     bool isConstructing() const;
 
     bool isEntryJSFrame() const;
--- a/js/src/ion/IonFrames.cpp
+++ b/js/src/ion/IonFrames.cpp
@@ -113,16 +113,25 @@ bool
 IonFrameIterator::isNative() const
 {
     if (type_ != IonFrame_Exit)
         return false;
     return exitFrame()->footer()->ionCode() == NULL;
 }
 
 bool
+IonFrameIterator::isDOMExit() const
+{
+    if (type_ != IonFrame_Exit)
+        return false;
+    IonCode *code = exitFrame()->footer()->ionCode();
+    return code == ION_FRAME_DOMGETTER || code == ION_FRAME_DOMSETTER;
+}
+
+bool
 IonFrameIterator::isFunctionFrame() const
 {
     return js::ion::CalleeTokenIsFunction(calleeToken());
 }
 
 bool
 IonFrameIterator::isEntryJSFrame() const
 {
@@ -493,16 +502,24 @@ MarkIonExitFrame(JSTracer *trc, const Io
     if (frame.isNative()) {
         IonNativeExitFrameLayout *native = frame.exitFrame()->nativeExit();
         size_t len = native->argc() + 2;
         Value *vp = native->vp();
         gc::MarkValueRootRange(trc, len, vp, "ion-native-args");
         return;
     }
 
+    if (frame.isDOMExit()) {
+        IonDOMExitFrameLayout *dom = frame.exitFrame()->DOMExit();
+        gc::MarkObjectRoot(trc, dom->thisObjAddress(), "ion-dom-args");
+        if (dom->isSetterFrame())
+            gc::MarkValueRoot(trc, dom->vp(), "ion-dom-args");
+        return;
+    }
+
     MarkIonCodeRoot(trc, footer->addressOfIonCode(), "ion-exit-code");
 
     const VMFunction *f = footer->function();
     if (f == NULL || f->explicitArgs == 0)
         return;
 
     // Mark arguments of the VM wrapper.
     uint8 *argBase = frame.exitFrame()->argBase();
--- a/js/src/ion/IonMacroAssembler.h
+++ b/js/src/ion/IonMacroAssembler.h
@@ -428,16 +428,22 @@ class MacroAssembler : public MacroAssem
         Push(ImmWord(f));
     }
     void enterFakeExitFrame() {
         linkExitFrame();
         Push(ImmWord(uintptr_t(NULL)));
         Push(ImmWord(uintptr_t(NULL)));
     }
 
+    void enterFakeDOMFrame(void *codeVal) {
+        linkExitFrame();
+        Push(ImmWord(uintptr_t(codeVal)));
+        Push(ImmWord(uintptr_t(NULL)));
+    }
+
     void leaveExitFrame() {
         freeStack(IonExitFooterFrame::Size());
     }
 
     void link(IonCode *code) {
 
         // If this code can transition to C++ code and witness a GC, then we need to store
         // the IonCode onto the stack in order to GC it correctly.  exitCodePatch should
--- a/js/src/ion/LIR-Common.h
+++ b/js/src/ion/LIR-Common.h
@@ -496,16 +496,87 @@ class LCallGeneric : public LCallInstruc
     const LAllocation *getNargsReg() {
         return getTemp(0)->output();
     }
     const LAllocation *getTempObject() {
         return getTemp(1)->output();
     }
 };
 
+template <size_t defs, size_t ops>
+class LDOMPropertyInstructionHelper : public LCallInstructionHelper<defs, 1 + ops, 3>
+{
+  protected:
+    LDOMPropertyInstructionHelper(const LDefinition &JSContextReg,
+                                  const LAllocation &ObjectReg,
+                                  const LDefinition &PrivReg,
+                                  const LDefinition &ValueReg)
+    {
+        this->setTemp(0, JSContextReg);
+        this->setTemp(1, PrivReg);
+        this->setTemp(2, ValueReg);
+
+        this->setOperand(0, ObjectReg);
+    }
+
+  public:
+
+    const LAllocation *getJSContextReg() {
+        return this->getTemp(0)->output();
+    }
+    const LAllocation *getObjectReg() {
+        return this->getOperand(0);
+    }
+    const LAllocation *getPrivReg() {
+        return this->getTemp(1)->output();
+    }
+    const LAllocation *getValueReg() {
+        return this->getTemp(2)->output();
+    }
+};
+
+
+class LGetDOMProperty : public LDOMPropertyInstructionHelper<BOX_PIECES, 0>
+{
+  public:
+    LIR_HEADER(GetDOMProperty);
+
+    LGetDOMProperty(const LDefinition &JSContextReg,
+                    const LAllocation &ObjectReg,
+                    const LDefinition &PrivReg,
+                    const LDefinition &ValueReg)
+      : LDOMPropertyInstructionHelper<BOX_PIECES, 0>(JSContextReg, ObjectReg,
+                                                     PrivReg, ValueReg)
+    { }
+
+    MGetDOMProperty *mir() const {
+        return mir_->toGetDOMProperty();
+    }
+};
+
+class LSetDOMProperty : public LDOMPropertyInstructionHelper<0, BOX_PIECES>
+{
+  public:
+    LIR_HEADER(SetDOMProperty);
+
+    LSetDOMProperty(const LDefinition &JSContextReg,
+                    const LAllocation &ObjectReg,
+                    const LDefinition &PrivReg,
+                    const LDefinition &ValueReg)
+      : LDOMPropertyInstructionHelper<0, BOX_PIECES>(JSContextReg, ObjectReg,
+                                                     PrivReg, ValueReg)
+    { }
+
+    static const size_t Value = 1;
+
+    MSetDOMProperty *mir() const {
+        return mir_->toSetDOMProperty();
+    }
+};
+
 // Generates a monomorphic callsite for a known, native target.
 class LCallNative : public LCallInstructionHelper<BOX_PIECES, 0, 4>
 {
     uint32 argslot_;
 
   public:
     LIR_HEADER(CallNative);
 
--- a/js/src/ion/LOpcodes.h
+++ b/js/src/ion/LOpcodes.h
@@ -154,17 +154,19 @@
     _(TypeOfV)                      \
     _(ToIdV)                        \
     _(Floor)                        \
     _(Round)                        \
     _(InstanceOfO)                  \
     _(InstanceOfV)                  \
     _(InterruptCheck)               \
     _(ProfilingEnter)               \
-    _(ProfilingExit)
+    _(ProfilingExit)                \
+    _(GetDOMProperty)               \
+    _(SetDOMProperty)
 
 #if defined(JS_CPU_X86)
 # include "x86/LOpcodes-x86.h"
 #elif defined(JS_CPU_X64)
 # include "x64/LOpcodes-x64.h"
 #elif defined(JS_CPU_ARM)
 # include "arm/LOpcodes-arm.h"
 #endif
--- a/js/src/ion/Lowering.cpp
+++ b/js/src/ion/Lowering.cpp
@@ -1804,16 +1804,43 @@ LIRGenerator::visitProfilingEnter(MProfi
 }
 
 bool
 LIRGenerator::visitProfilingExit(MProfilingExit *ins)
 {
     return add(new LProfilingExit(temp()), ins);
 }
 
+bool
+LIRGenerator::visitSetDOMProperty(MSetDOMProperty *ins)
+{
+    MDefinition *val = ins->value();
+
+    LSetDOMProperty *lir = new LSetDOMProperty(tempFixed(CallTempReg0),
+                                               useFixed(ins->object(), CallTempReg1),
+                                               tempFixed(CallTempReg2),
+                                               tempFixed(CallTempReg3));
+    if (!useBox(lir, LSetDOMProperty::Value, val))
+        return false;
+
+    return add(lir, ins) && assignSafepoint(lir, ins);
+}
+
+bool
+LIRGenerator::visitGetDOMProperty(MGetDOMProperty *ins)
+{
+    LGetDOMProperty *lir = new LGetDOMProperty(tempFixed(CallTempReg0),
+                                               useFixed(ins->object(), CallTempReg1),
+                                               tempFixed(CallTempReg2),
+                                               tempFixed(CallTempReg3));
+
+    return defineReturn(lir, ins) && assignSafepoint(lir, ins);
+}
+
+
 static void
 SpewResumePoint(MBasicBlock *block, MInstruction *ins, MResumePoint *resumePoint)
 {
     fprintf(IonSpewFile, "Current resume point %p details:\n", (void *)resumePoint);
     fprintf(IonSpewFile, "    frame count: %u\n", resumePoint->frameCount());
 
     if (ins) {
         fprintf(IonSpewFile, "    taken after: ");
--- a/js/src/ion/Lowering.h
+++ b/js/src/ion/Lowering.h
@@ -172,15 +172,17 @@ class LIRGenerator : public LIRGenerator
     bool visitIteratorEnd(MIteratorEnd *ins);
     bool visitStringLength(MStringLength *ins);
     bool visitArgumentsLength(MArgumentsLength *ins);
     bool visitGetArgument(MGetArgument *ins);
     bool visitThrow(MThrow *ins);
     bool visitInstanceOf(MInstanceOf *ins);
     bool visitProfilingEnter(MProfilingEnter *ins);
     bool visitProfilingExit(MProfilingExit *ins);
+    bool visitSetDOMProperty(MSetDOMProperty *ins);
+    bool visitGetDOMProperty(MGetDOMProperty *ins);
 };
 
 } // namespace ion
 } // namespace js
 
 #endif // jsion_ion_lowering_h__
 
--- a/js/src/ion/MIR.h
+++ b/js/src/ion/MIR.h
@@ -4768,16 +4768,93 @@ class MCallSetElement
     MDefinition *index() const {
         return getOperand(1);
     }
     MDefinition *value() const {
         return getOperand(2);
     }
 };
 
+class MSetDOMProperty
+  : public MAryInstruction<2>,
+    public MixPolicy<ObjectPolicy<0>, BoxPolicy<1> >
+{
+    const JSJitPropertyOp func_;
+
+    MSetDOMProperty(const JSJitPropertyOp func, MDefinition *obj, MDefinition *val)
+      : func_(func)
+    {
+        initOperand(0, obj);
+        initOperand(1, val);
+    }
+
+  public:
+    INSTRUCTION_HEADER(SetDOMProperty);
+
+    static MSetDOMProperty *New(const JSJitPropertyOp func, MDefinition *obj, MDefinition *val)
+    {
+        return new MSetDOMProperty(func, obj, val);
+    }
+
+    const JSJitPropertyOp fun() {
+        return func_;
+    }
+
+    MDefinition *object() {
+        return getOperand(0);
+    }
+
+    MDefinition *value()
+    {
+        return getOperand(1);
+    }
+
+    TypePolicy *typePolicy() {
+        return this;
+    }
+};
+
+class MGetDOMProperty
+  : public MAryInstruction<1>,
+    public ObjectPolicy<0>
+{
+    const JSJitPropertyOp func_;
+    bool isInfallible_;
+
+    MGetDOMProperty(const JSJitPropertyOp func, MDefinition *obj, bool isInfallible)
+      : func_(func), isInfallible_(isInfallible)
+    {
+        initOperand(0, obj);
+
+        setResultType(MIRType_Value);
+    }
+
+  public:
+    INSTRUCTION_HEADER(GetDOMProperty);
+
+    static MGetDOMProperty *New(const JSJitPropertyOp func, MDefinition *obj, bool isInfallible)
+    {
+        return new MGetDOMProperty(func, obj, isInfallible);
+    }
+
+    const JSJitPropertyOp fun() {
+        return func_;
+    }
+    bool isInfallible() {
+        return isInfallible_;
+    }
+    MDefinition *object() {
+        return getOperand(0);
+    }
+
+    TypePolicy *typePolicy() {
+        return this;
+    }
+};
+
 class MStringLength
   : public MUnaryInstruction,
     public StringPolicy
 {
     MStringLength(MDefinition *string)
       : MUnaryInstruction(string)
     {
         setResultType(MIRType_Int32);
--- a/js/src/ion/MOpcodes.h
+++ b/js/src/ion/MOpcodes.h
@@ -122,17 +122,19 @@ namespace ion {
     _(StringLength)                                                         \
     _(ArgumentsLength)                                                      \
     _(GetArgument)                                                          \
     _(Floor)                                                                \
     _(Round)                                                                \
     _(InstanceOf)                                                           \
     _(InterruptCheck)                                                       \
     _(ProfilingEnter)                                                       \
-    _(ProfilingExit)
+    _(ProfilingExit)                                                        \
+    _(GetDOMProperty)                                                       \
+    _(SetDOMProperty)
 
 // Forward declarations of MIR types.
 #define FORWARD_DECLARE(op) class M##op;
  MIR_OPCODE_LIST(FORWARD_DECLARE)
 #undef FORWARD_DECLARE
 
 class MInstructionVisitor
 {
--- a/js/src/ion/arm/IonFrames-arm.h
+++ b/js/src/ion/arm/IonFrames-arm.h
@@ -3,16 +3,18 @@
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef jsion_ionframes_arm_h__
 #define jsion_ionframes_arm_h__
 
+#include "ion/shared/IonFrames-shared.h"
+
 namespace js {
 namespace ion {
 
 class IonFramePrefix;
 // Layout of the frame prefix. This assumes the stack architecture grows down.
 // If this is ever not the case, we'll have to refactor.
 class IonCommonFrameLayout
 {
@@ -149,16 +151,17 @@ class IonOsrFrameLayout : public IonJSFr
 {
   public:
     static inline size_t Size() {
         return sizeof(IonOsrFrameLayout);
     }
 };
 
 class IonNativeExitFrameLayout;
+class IonDOMExitFrameLayout;
 
 // this is the frame layout when we are exiting ion code, and about to enter EABI code
 class IonExitFrameLayout : public IonCommonFrameLayout
 {
     inline uint8 *top() {
         return reinterpret_cast<uint8 *>(this + 1);
     }
 
@@ -182,19 +185,24 @@ class IonExitFrameLayout : public IonCom
         JS_ASSERT(footer()->ionCode() != NULL);
         return top();
     }
     inline IonNativeExitFrameLayout *nativeExit() {
         // see CodeGenerator::visitCallNative
         JS_ASSERT(footer()->ionCode() == NULL);
         return reinterpret_cast<IonNativeExitFrameLayout *>(footer());
     }
+    inline IonDOMExitFrameLayout *DOMExit() {
+        JS_ASSERT(footer()->ionCode() == ION_FRAME_DOMGETTER ||
+                  footer()->ionCode() == ION_FRAME_DOMSETTER);
+        return reinterpret_cast<IonDOMExitFrameLayout *>(footer());
+    }
 };
 
-// Cannot inherit implementation since we need to extend the top of
+// Cannot inherit implementa<tion since we need to extend the top of
 // IonExitFrameLayout.
 class IonNativeExitFrameLayout
 {
     IonExitFooterFrame footer_;
     IonExitFrameLayout exit_;
     uintptr_t argc_;
 
     // We need to split the Value into 2 fields of 32 bits, otherwise the C++
@@ -213,16 +221,46 @@ class IonNativeExitFrameLayout
     inline Value *vp() {
         return reinterpret_cast<Value*>(&loCalleeResult_);
     }
     inline uintptr_t argc() const {
         return argc_;
     }
 };
 
+class IonDOMExitFrameLayout
+{
+    IonExitFooterFrame footer_;
+    IonExitFrameLayout exit_;
+    JSObject *thisObj;
+
+    // We need to split the Value in 2 field of 32 bits, otherwise the C++
+    // compiler may add some padding between the fields.
+    uint32_t loCalleeResult_;
+    uint32_t hiCalleeResult_;
+
+  public:
+    static inline size_t Size() {
+        return sizeof(IonDOMExitFrameLayout);
+    }
+
+    static size_t offsetOfResult() {
+        return offsetof(IonDOMExitFrameLayout, loCalleeResult_);
+    }
+    inline Value *vp() {
+        return reinterpret_cast<Value*>(&loCalleeResult_);
+    }
+    inline JSObject **thisObjAddress() {
+        return &thisObj;
+    }
+    inline bool isSetterFrame() {
+        return footer_.ionCode() == ION_FRAME_DOMSETTER;
+    }
+};
+
 // An invalidation bailout stack is at the stack pointer for the callee frame.
 class InvalidationBailoutStack
 {
     double      fpregs_[FloatRegisters::Total];
     uintptr_t   regs_[Registers::Total];
     IonScript   *ionScript_;
     uint8       *osiPointReturnAddress_;
 
--- a/js/src/ion/shared/CodeGenerator-shared-inl.h
+++ b/js/src/ion/shared/CodeGenerator-shared-inl.h
@@ -97,16 +97,29 @@ GetValueOutput(LInstruction *ins)
                         ToRegister(ins->getDef(PAYLOAD_INDEX)));
 #elif defined(JS_PUNBOX64)
     return ValueOperand(ToRegister(ins->getDef(0)));
 #else
 #error "Unknown"
 #endif
 }
 
+static inline ValueOperand
+GetTempValue(const Register &type, const Register &payload)
+{
+#if defined(JS_NUNBOX32)
+    return ValueOperand(type, payload);
+#elif defined(JS_PUNBOX64)
+    (void)type;
+    return ValueOperand(payload);
+#else
+#error "Unknown"
+#endif
+}
+
 static inline Assembler::Condition
 JSOpToCondition(JSOp op)
 {
     switch (op) {
       case JSOP_EQ:
       case JSOP_STRICTEQ:
         return Assembler::Equal;
       case JSOP_NE:
new file mode 100644
--- /dev/null
+++ b/js/src/ion/shared/IonFrames-shared.h
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=4 sw=4 et tw=79:
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Communicator client code, released
+ * March 31, 1998.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   David Anderson <dvander@alliedmods.net>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+#ifndef jsion_ionframes_shared_h__
+#define jsion_ionframes_shared_h__
+
+#define ION_FRAME_DOMGETTER ((IonCode *)0x1)
+#define ION_FRAME_DOMSETTER ((IonCode *)0x2)
+
+#endif
--- a/js/src/ion/shared/IonFrames-x86-shared.h
+++ b/js/src/ion/shared/IonFrames-x86-shared.h
@@ -3,16 +3,18 @@
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef js_ion_frame_layouts_x86_h__
 #define js_ion_frame_layouts_x86_h__
 
+#include "ion/shared/IonFrames-shared.h"
+
 namespace js {
 namespace ion {
 
 class IonCommonFrameLayout
 {
   private:
     uint8 *returnAddress_;
     uintptr_t descriptor_;
@@ -134,16 +136,17 @@ class IonExitFooterFrame
 
     // This should only be called for function()->outParam == Type_Handle
     Value *outVp() {
         return reinterpret_cast<Value *>(reinterpret_cast<char *>(this) - sizeof(Value));
     }
 };
 
 class IonNativeExitFrameLayout;
+class IonDOMExitFrameLayout;
 
 class IonExitFrameLayout : public IonCommonFrameLayout
 {
     inline uint8 *top() {
         return reinterpret_cast<uint8 *>(this + 1);
     }
 
   public:
@@ -166,16 +169,21 @@ class IonExitFrameLayout : public IonCom
         JS_ASSERT(footer()->ionCode() != NULL);
         return top();
     }
     inline IonNativeExitFrameLayout *nativeExit() {
         // see CodeGenerator::visitCallNative
         JS_ASSERT(footer()->ionCode() == NULL);
         return reinterpret_cast<IonNativeExitFrameLayout *>(footer());
     }
+    inline IonDOMExitFrameLayout *DOMExit() {
+        JS_ASSERT(footer()->ionCode() == ION_FRAME_DOMGETTER ||
+                  footer()->ionCode() == ION_FRAME_DOMSETTER);
+        return reinterpret_cast<IonDOMExitFrameLayout *>(footer());
+    }
 };
 
 class IonNativeExitFrameLayout
 {
     IonExitFooterFrame footer_;
     IonExitFrameLayout exit_;
     uintptr_t argc_;
 
@@ -195,16 +203,46 @@ class IonNativeExitFrameLayout
     inline Value *vp() {
         return reinterpret_cast<Value*>(&loCalleeResult_);
     }
     inline uintptr_t argc() const {
         return argc_;
     }
 };
 
+class IonDOMExitFrameLayout
+{
+    IonExitFooterFrame footer_;
+    IonExitFrameLayout exit_;
+    JSObject *thisObj;
+
+    // We need to split the Value in 2 field of 32 bits, otherwise the C++
+    // compiler may add some padding between the fields.
+    uint32_t loCalleeResult_;
+    uint32_t hiCalleeResult_;
+
+  public:
+    static inline size_t Size() {
+        return sizeof(IonDOMExitFrameLayout);
+    }
+
+    static size_t offsetOfResult() {
+        return offsetof(IonDOMExitFrameLayout, loCalleeResult_);
+    }
+    inline Value *vp() {
+        return reinterpret_cast<Value*>(&loCalleeResult_);
+    }
+    inline JSObject **thisObjAddress() {
+        return &thisObj;
+    }
+    inline bool isSetterFrame() {
+        return footer_.ionCode() == ION_FRAME_DOMSETTER;
+    }
+};
+
 class IonOsrFrameLayout : public IonJSFrameLayout
 {
   public:
     static inline size_t Size() {
         return sizeof(IonOsrFrameLayout);
     }
  };