Bug 945223 - Ensure correct baseline stack when calling out to noSuchMethod, so that decompiler works. r=efaust
authorKannan Vijayan <kvijayan@mozilla.com>
Tue, 10 Dec 2013 14:53:49 -0500
changeset 159785 d4f4d11a99e3ee3a5e792bdd17ea4cf72e1daff4
parent 159784 5b48376ee2d899d48ae140c555f02f6f871bb39c
child 159786 982196bdc263ccb085a082aedb9ac102206d8db3
push id25813
push userkwierso@gmail.com
push dateWed, 11 Dec 2013 00:42:57 +0000
treeherdermozilla-central@3ea3d3baa67b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersefaust
bugs945223
milestone29.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 945223 - Ensure correct baseline stack when calling out to noSuchMethod, so that decompiler works. r=efaust
js/src/jit-test/tests/baseline/bug945223.js
js/src/jit/BaselineIC.cpp
js/src/jit/arm/BaselineHelpers-arm.h
js/src/jit/x64/BaselineHelpers-x64.h
js/src/jit/x86/BaselineHelpers-x86.h
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/baseline/bug945223.js
@@ -0,0 +1,12 @@
+// |jit-test| error: InternalError
+
+Array.prototype.__proto__ = Proxy.create({
+    getPropertyDescriptor: function(name) {
+	return (560566);
+    },
+}, null);
+function f() {}
+function g() {  }    
+var x = [f,f,f,undefined,g];
+for (var i = 0; i < 5; ++i)
+  y = x[i]("x");
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -4291,29 +4291,41 @@ ICGetElemNativeCompiler::generateStubCod
 #if JS_HAS_NO_SUCH_METHOD
         if (isCallElem_) {
             Label afterNoSuchMethod;
             Label skipNoSuchMethod;
 
             masm.branchTestUndefined(Assembler::NotEqual, valAddr, &skipNoSuchMethod);
 
             GeneralRegisterSet regs = availableGeneralRegs(0);
+            regs.take(R1);
+            regs.take(R0);
             regs.takeUnchecked(objReg);
-            regs.take(R1);
-            Register scratch = regs.takeAnyExcluding(BaselineTailCallReg);
             if (popR1)
                 masm.pop(R1.scratchReg());
-            enterStubFrame(masm, scratch);
+
+            // Box and push obj and key onto baseline frame stack for decompiler.
+            masm.tagValue(JSVAL_TYPE_OBJECT, objReg, R0);
+            EmitStowICValues(masm, 2);
+
+            regs.add(R0);
+            regs.takeUnchecked(objReg);
+
+            enterStubFrame(masm, regs.getAnyExcluding(BaselineTailCallReg));
 
             masm.pushValue(R1);
             masm.push(objReg);
             if (!callVM(LookupNoSuchMethodHandlerInfo, masm))
                 return false;
 
             leaveStubFrame(masm);
+
+            // Pop pushed obj and key from baseline stack.
+            EmitUnstowICValues(masm, 2, /* discard = */ true);
+
             // Result is already in R0
             masm.jump(&afterNoSuchMethod);
             masm.bind(&skipNoSuchMethod);
 
             if (popR1)
                 masm.pop(R1.scratchReg());
             masm.loadValue(valAddr, R0);
             masm.bind(&afterNoSuchMethod);
@@ -4467,34 +4479,48 @@ ICGetElem_Dense::Compiler::generateStubC
 #if JS_HAS_NO_SUCH_METHOD
     entersStubFrame_ = true;
     if (isCallElem_) {
         Label afterNoSuchMethod;
         Label skipNoSuchMethod;
         regs = availableGeneralRegs(0);
         regs.takeUnchecked(obj);
         regs.takeUnchecked(key);
+        regs.takeUnchecked(BaselineTailCallReg);
         ValueOperand val = regs.takeValueOperand();
 
         masm.loadValue(element, val);
         masm.branchTestUndefined(Assembler::NotEqual, val, &skipNoSuchMethod);
+
+        // Box and push obj and key onto baseline frame stack for decompiler.
+        EmitRestoreTailCallReg(masm);
+        masm.tagValue(JSVAL_TYPE_OBJECT, obj, val);
+        masm.pushValue(val);
+        masm.tagValue(JSVAL_TYPE_INT32, key, val);
+        masm.pushValue(val);
+        EmitRepushTailCallReg(masm);
+
         regs.add(val);
 
         // Call __noSuchMethod__ checker.  Object pointer is in objReg.
         enterStubFrame(masm, regs.getAnyExcluding(BaselineTailCallReg));
 
         regs.take(val);
 
         masm.tagValue(JSVAL_TYPE_INT32, key, val);
         masm.pushValue(val);
         masm.push(obj);
         if (!callVM(LookupNoSuchMethodHandlerInfo, masm))
             return false;
 
         leaveStubFrame(masm);
+
+        // Pop pushed obj and key from baseline stack.
+        EmitUnstowICValues(masm, 2, /* discard = */ true);
+
         // Result is already in R0
         masm.jump(&afterNoSuchMethod);
         masm.bind(&skipNoSuchMethod);
 
         masm.moveValue(val, R0);
         masm.bind(&afterNoSuchMethod);
     } else {
         masm.loadValue(element, R0);
@@ -4698,29 +4724,43 @@ ICGetElem_Arguments::Compiler::generateS
     if (isCallElem_) {
         Label afterNoSuchMethod;
         Label skipNoSuchMethod;
 
         masm.branchTestUndefined(Assembler::NotEqual, tempVal, &skipNoSuchMethod);
 
         // Call __noSuchMethod__ checker.  Object pointer is in objReg.
         regs = availableGeneralRegs(0);
-        // R1 and objReg are guaranteed not to overlap.
         regs.takeUnchecked(objReg);
-        regs.take(R1);
-        masm.tagValue(JSVAL_TYPE_INT32, idxReg, R1);
-        scratchReg = regs.takeAnyExcluding(BaselineTailCallReg);
-        enterStubFrame(masm, scratchReg);
-
-        masm.pushValue(R1);
+        regs.takeUnchecked(idxReg);
+        regs.takeUnchecked(BaselineTailCallReg);
+        ValueOperand val = regs.takeValueOperand();
+
+        // Box and push obj and key onto baseline frame stack for decompiler.
+        EmitRestoreTailCallReg(masm);
+        masm.tagValue(JSVAL_TYPE_OBJECT, objReg, val);
+        masm.pushValue(val);
+        masm.tagValue(JSVAL_TYPE_INT32, idxReg, val);
+        masm.pushValue(val);
+        EmitRepushTailCallReg(masm);
+
+        regs.add(val);
+        enterStubFrame(masm, regs.getAnyExcluding(BaselineTailCallReg));
+        regs.take(val);
+
+        masm.pushValue(val);
         masm.push(objReg);
         if (!callVM(LookupNoSuchMethodHandlerInfo, masm))
             return false;
 
         leaveStubFrame(masm);
+
+        // Pop pushed obj and key from baseline stack.
+        EmitUnstowICValues(masm, 2, /* discard = */ true);
+
         // Result is already in R0
         masm.jump(&afterNoSuchMethod);
         masm.bind(&skipNoSuchMethod);
 
         masm.moveValue(tempVal, R0);
         masm.bind(&afterNoSuchMethod);
     } else {
         masm.moveValue(tempVal, R0);
@@ -6528,26 +6568,43 @@ ICGetPropNativeCompiler::generateStubCod
         Label afterNoSuchMethod;
         Label skipNoSuchMethod;
 
         masm.push(objReg);
         masm.loadValue(result, R0);
         masm.branchTestUndefined(Assembler::NotEqual, R0, &skipNoSuchMethod);
 
         masm.pop(objReg);
-        enterStubFrame(masm, scratch);
-
-        masm.movePtr(ImmGCPtr(propName_.get()), R1.scratchReg());
-        masm.tagValue(JSVAL_TYPE_STRING, R1.scratchReg(), R1);
-        masm.pushValue(R1);
+
+        // Call __noSuchMethod__ checker.  Object pointer is in objReg.
+        regs = availableGeneralRegs(0);
+        regs.takeUnchecked(objReg);
+        regs.takeUnchecked(BaselineTailCallReg);
+        ValueOperand val = regs.takeValueOperand();
+
+        // Box and push obj onto baseline frame stack for decompiler.
+        EmitRestoreTailCallReg(masm);
+        masm.tagValue(JSVAL_TYPE_OBJECT, objReg, val);
+        masm.pushValue(val);
+        EmitRepushTailCallReg(masm);
+
+        enterStubFrame(masm, regs.getAnyExcluding(BaselineTailCallReg));
+
+        masm.movePtr(ImmGCPtr(propName_.get()), val.scratchReg());
+        masm.tagValue(JSVAL_TYPE_STRING, val.scratchReg(), val);
+        masm.pushValue(val);
         masm.push(objReg);
         if (!callVM(LookupNoSuchMethodHandlerInfo, masm))
             return false;
 
         leaveStubFrame(masm);
+
+        // Pop pushed obj from baseline stack.
+        EmitUnstowICValues(masm, 1, /* discard = */ true);
+
         masm.jump(&afterNoSuchMethod);
         masm.bind(&skipNoSuchMethod);
 
         // Pop pushed objReg.
         masm.addPtr(Imm32(sizeof(void *)), BaselineStackReg);
         masm.bind(&afterNoSuchMethod);
     } else {
         masm.loadValue(result, R0);
--- a/js/src/jit/arm/BaselineHelpers-arm.h
+++ b/js/src/jit/arm/BaselineHelpers-arm.h
@@ -21,16 +21,22 @@ static const size_t ICStackValueOffset =
 
 inline void
 EmitRestoreTailCallReg(MacroAssembler &masm)
 {
     // No-op on ARM because link register is always holding the return address.
 }
 
 inline void
+EmitRepushTailCallReg(MacroAssembler &masm)
+{
+    // No-op on ARM because link register is always holding the return address.
+}
+
+inline void
 EmitCallIC(CodeOffsetLabel *patchOffset, MacroAssembler &masm)
 {
     // Move ICEntry offset into BaselineStubReg
     CodeOffsetLabel offset = masm.movWithPatch(ImmWord(-1), BaselineStubReg);
     *patchOffset = offset;
 
     // Load stub pointer into BaselineStubReg
     masm.loadPtr(Address(BaselineStubReg, ICEntry::offsetOfFirstStub()), BaselineStubReg);
@@ -191,28 +197,35 @@ EmitStowICValues(MacroAssembler &masm, i
         // Stow R0 and R1
         masm.pushValue(R0);
         masm.pushValue(R1);
         break;
     }
 }
 
 inline void
-EmitUnstowICValues(MacroAssembler &masm, int values)
+EmitUnstowICValues(MacroAssembler &masm, int values, bool discard = false)
 {
     JS_ASSERT(values >= 0 && values <= 2);
     switch(values) {
       case 1:
         // Unstow R0
-        masm.popValue(R0);
+        if (discard)
+            masm.addPtr(Imm32(sizeof(Value)), BaselineStackReg);
+        else
+            masm.popValue(R0);
         break;
       case 2:
         // Unstow R0 and R1
-        masm.popValue(R1);
-        masm.popValue(R0);
+        if (discard) {
+            masm.addPtr(Imm32(sizeof(Value) * 2), BaselineStackReg);
+        } else {
+            masm.popValue(R1);
+            masm.popValue(R0);
+        }
         break;
     }
 }
 
 inline void
 EmitCallTypeUpdateIC(MacroAssembler &masm, IonCode *code, uint32_t objectOffset)
 {
     JS_ASSERT(R2 == ValueOperand(r1, r0));
--- a/js/src/jit/x64/BaselineHelpers-x64.h
+++ b/js/src/jit/x64/BaselineHelpers-x64.h
@@ -21,16 +21,22 @@ static const size_t ICStackValueOffset =
 
 inline void
 EmitRestoreTailCallReg(MacroAssembler &masm)
 {
     masm.pop(BaselineTailCallReg);
 }
 
 inline void
+EmitRepushTailCallReg(MacroAssembler &masm)
+{
+    masm.push(BaselineTailCallReg);
+}
+
+inline void
 EmitCallIC(CodeOffsetLabel *patchOffset, MacroAssembler &masm)
 {
     // Move ICEntry offset into BaselineStubReg
     CodeOffsetLabel offset = masm.movWithPatch(ImmWord(-1), BaselineStubReg);
     *patchOffset = offset;
 
     // Load stub pointer into BaselineStubReg
     masm.loadPtr(Address(BaselineStubReg, (int32_t) ICEntry::offsetOfFirstStub()),
@@ -177,31 +183,38 @@ EmitStowICValues(MacroAssembler &masm, i
         masm.pushValue(R0);
         masm.pushValue(R1);
         masm.push(BaselineTailCallReg);
         break;
     }
 }
 
 inline void
-EmitUnstowICValues(MacroAssembler &masm, int values)
+EmitUnstowICValues(MacroAssembler &masm, int values, bool discard = false)
 {
     JS_ASSERT(values >= 0 && values <= 2);
     switch(values) {
       case 1:
         // Unstow R0
         masm.pop(BaselineTailCallReg);
-        masm.popValue(R0);
+        if (discard)
+            masm.addPtr(Imm32(sizeof(Value)), BaselineStackReg);
+        else
+            masm.popValue(R0);
         masm.push(BaselineTailCallReg);
         break;
       case 2:
         // Unstow R0 and R1
         masm.pop(BaselineTailCallReg);
-        masm.popValue(R1);
-        masm.popValue(R0);
+        if (discard) {
+            masm.addPtr(Imm32(sizeof(Value) * 2), BaselineStackReg);
+        } else {
+            masm.popValue(R1);
+            masm.popValue(R0);
+        }
         masm.push(BaselineTailCallReg);
         break;
     }
 }
 
 inline void
 EmitCallTypeUpdateIC(MacroAssembler &masm, IonCode *code, uint32_t objectOffset)
 {
--- a/js/src/jit/x86/BaselineHelpers-x86.h
+++ b/js/src/jit/x86/BaselineHelpers-x86.h
@@ -21,16 +21,22 @@ static const size_t ICStackValueOffset =
 
 inline void
 EmitRestoreTailCallReg(MacroAssembler &masm)
 {
     masm.pop(BaselineTailCallReg);
 }
 
 inline void
+EmitRepushTailCallReg(MacroAssembler &masm)
+{
+    masm.push(BaselineTailCallReg);
+}
+
+inline void
 EmitCallIC(CodeOffsetLabel *patchOffset, MacroAssembler &masm)
 {
     // Move ICEntry offset into BaselineStubReg
     CodeOffsetLabel offset = masm.movWithPatch(ImmWord(-1), BaselineStubReg);
     *patchOffset = offset;
 
     // Load stub pointer into BaselineStubReg
     masm.loadPtr(Address(BaselineStubReg, (int32_t) ICEntry::offsetOfFirstStub()),
@@ -183,31 +189,38 @@ EmitStowICValues(MacroAssembler &masm, i
         masm.pushValue(R0);
         masm.pushValue(R1);
         masm.push(BaselineTailCallReg);
         break;
     }
 }
 
 inline void
-EmitUnstowICValues(MacroAssembler &masm, int values)
+EmitUnstowICValues(MacroAssembler &masm, int values, bool discard = false)
 {
     JS_ASSERT(values >= 0 && values <= 2);
     switch(values) {
       case 1:
         // Unstow R0
         masm.pop(BaselineTailCallReg);
-        masm.popValue(R0);
+        if (discard)
+            masm.addPtr(Imm32(sizeof(Value)), BaselineStackReg);
+        else
+            masm.popValue(R0);
         masm.push(BaselineTailCallReg);
         break;
       case 2:
         // Unstow R0 and R1
         masm.pop(BaselineTailCallReg);
-        masm.popValue(R1);
-        masm.popValue(R0);
+        if (discard) {
+            masm.addPtr(Imm32(sizeof(Value) * 2), BaselineStackReg);
+        } else {
+            masm.popValue(R1);
+            masm.popValue(R0);
+        }
         masm.push(BaselineTailCallReg);
         break;
     }
 }
 
 inline void
 EmitCallTypeUpdateIC(MacroAssembler &masm, IonCode *code, uint32_t objectOffset)
 {