Bug 1100337 - Cheat when computing resume address for propagating exception for debug mode in Ion exception handler. (r=jandem)
authorShu-yu Guo <shu@rfrn.org>
Wed, 26 Nov 2014 13:35:57 -0800
changeset 217776 df2462ab460bfe4b873043214830c540425c45b9
parent 217775 b20b3bb15bf732fd652600d36cbb5aad193a2aa1
child 217777 4dffd706f975dd3aa0a63330f2d37acb22674b5b
push id27890
push usercbook@mozilla.com
push dateThu, 27 Nov 2014 11:55:59 +0000
treeherdermozilla-central@8d185a31024e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs1100337
milestone36.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 1100337 - Cheat when computing resume address for propagating exception for debug mode in Ion exception handler. (r=jandem)
js/src/jit-test/tests/debug/onExceptionUnwind-14.js
js/src/jit/BaselineBailouts.cpp
js/src/jit/BaselineJIT.cpp
js/src/jit/BaselineJIT.h
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/onExceptionUnwind-14.js
@@ -0,0 +1,23 @@
+var g = newGlobal();
+var dbg = new Debugger(g);
+
+g.eval("" + function f() {
+  throw 42;
+});
+
+g.eval("" + function g() {
+  throw new Error("42");
+});
+
+// Call the functions once. This will compile them in Ion under --ion-eager.
+g.eval("try { f(); } catch (e) { }");
+g.eval("try { g(); } catch (e) { }");
+
+// Now set an onExceptionUnwind hook so that the Ion-compiled functions will
+// try to bail out. The tail of the bytecode for f and g looks like 'throw;
+// retrval', with 'retrval' being unreachable. Since 'throw' is resumeAfter,
+// bailing out for debug mode will attempt to resume at 'retrval'. Test that
+// this case is handled.
+dbg.onExceptionUnwind = function f() { };
+g.eval("try { f(); } catch (e) { }");
+g.eval("try { g(); } catch (e) { }");
--- a/js/src/jit/BaselineBailouts.cpp
+++ b/js/src/jit/BaselineBailouts.cpp
@@ -1014,41 +1014,65 @@ InitFromBailout(JSContext *cx, HandleScr
             builder.setResumeAddr(baselineScript->returnAddressForIC(icEntry));
             builder.setMonitorStub(firstMonStub);
             JitSpew(JitSpew_BaselineBailouts, "      Set resumeAddr=%p monitorStub=%p",
                     baselineScript->returnAddressForIC(icEntry), firstMonStub);
 
         } else {
             // If needed, initialize BaselineBailoutInfo's valueR0 and/or valueR1 with the
             // top stack values.
+            //
+            // Note that we use the 'maybe' variant of nativeCodeForPC because
+            // of exception propagation for debug mode. See note below.
             PCMappingSlotInfo slotInfo;
-            uint8_t *nativeCodeForPC = baselineScript->nativeCodeForPC(script, pc, &slotInfo);
-            unsigned numUnsynced = slotInfo.numUnsynced();
+            uint8_t *nativeCodeForPC = baselineScript->maybeNativeCodeForPC(script, pc, &slotInfo);
+            unsigned numUnsynced;
+
+            if (excInfo && excInfo->propagatingIonExceptionForDebugMode()) {
+                // When propagating an exception for debug mode, set the
+                // return address as the return-from-IC for the throw, so that
+                // Debugger hooks report the correct pc offset of the throwing
+                // op instead of its successor.
+                //
+                // Note that we never resume into this address, it is set for
+                // the sake of frame iterators giving the correct answer.
+                ICEntry &icEntry = baselineScript->anyKindICEntryFromPCOffset(iter.pcOffset());
+                nativeCodeForPC = baselineScript->returnAddressForIC(icEntry);
+
+                // The pc after the throwing PC could be unreachable, in which
+                // case we have no native code for it and no slot info. But in
+                // that case, there are definitely no unsynced slots.
+                numUnsynced = nativeCodeForPC ? slotInfo.numUnsynced() : 0;
+            } else {
+                MOZ_ASSERT(nativeCodeForPC);
+                numUnsynced = slotInfo.numUnsynced();
+            }
+
             MOZ_ASSERT(numUnsynced <= 2);
             PCMappingSlotInfo::SlotLocation loc1, loc2;
             if (numUnsynced > 0) {
                 loc1 = slotInfo.topSlotLocation();
                 JitSpew(JitSpew_BaselineBailouts, "      Popping top stack value into %d.",
-                            (int) loc1);
+                        (int) loc1);
                 builder.popValueInto(loc1);
             }
             if (numUnsynced > 1) {
                 loc2 = slotInfo.nextSlotLocation();
                 JitSpew(JitSpew_BaselineBailouts, "      Popping next stack value into %d.",
-                            (int) loc2);
+                        (int) loc2);
                 MOZ_ASSERT_IF(loc1 != PCMappingSlotInfo::SlotIgnore, loc1 != loc2);
                 builder.popValueInto(loc2);
             }
 
             // Need to adjust the frameSize for the frame to match the values popped
             // into registers.
             frameSize -= sizeof(Value) * numUnsynced;
             blFrame->setFrameSize(frameSize);
             JitSpew(JitSpew_BaselineBailouts, "      Adjusted framesize -= %d: %d",
-                            int(sizeof(Value) * numUnsynced), int(frameSize));
+                    int(sizeof(Value) * numUnsynced), int(frameSize));
 
             // If scopeChain is nullptr, then bailout is occurring during argument check.
             // In this case, resume into the prologue.
             uint8_t *opReturnAddr;
             if (scopeChain == nullptr) {
                 // Global and eval scripts expect the scope chain in R1, so only
                 // resume into the prologue for function scripts.
                 MOZ_ASSERT(fun);
--- a/js/src/jit/BaselineJIT.cpp
+++ b/js/src/jit/BaselineJIT.cpp
@@ -549,34 +549,55 @@ BaselineScript::icEntryFromReturnOffset(
 }
 
 uint8_t *
 BaselineScript::returnAddressForIC(const ICEntry &ent)
 {
     return method()->raw() + ent.returnOffset().offset();
 }
 
-ICEntry &
-BaselineScript::icEntryFromPCOffset(uint32_t pcOffset)
+static inline
+size_t ComputeBinarySearchMid(BaselineScript *baseline, uint32_t pcOffset)
 {
-    // Multiple IC entries can have the same PC offset, but this method only looks for
-    // those which have isForOp() set.
     size_t bottom = 0;
-    size_t top = numICEntries();
+    size_t top = baseline->numICEntries();
     size_t mid = bottom + (top - bottom) / 2;
     while (mid < top) {
-        ICEntry &midEntry = icEntry(mid);
+        ICEntry &midEntry = baseline->icEntry(mid);
         if (midEntry.pcOffset() < pcOffset)
             bottom = mid + 1;
         else if (midEntry.pcOffset() > pcOffset)
             top = mid;
         else
             break;
         mid = bottom + (top - bottom) / 2;
     }
+    return mid;
+}
+
+ICEntry &
+BaselineScript::anyKindICEntryFromPCOffset(uint32_t pcOffset)
+{
+    size_t mid = ComputeBinarySearchMid(this, pcOffset);
+
+    // Return any IC entry with a matching PC offset.
+    for (size_t i = mid; i < numICEntries() && icEntry(i).pcOffset() == pcOffset; i--)
+            return icEntry(i);
+    for (size_t i = mid+1; i < numICEntries() && icEntry(i).pcOffset() == pcOffset; i++)
+        return icEntry(i);
+    MOZ_CRASH("Invalid PC offset for IC entry.");
+}
+
+ICEntry &
+BaselineScript::icEntryFromPCOffset(uint32_t pcOffset)
+{
+    // Multiple IC entries can have the same PC offset, but this method only looks for
+    // those which have isForOp() set.
+    size_t mid = ComputeBinarySearchMid(this, pcOffset);
+
     // Found an IC entry with a matching PC offset.  Search backward, and then
     // forward from this IC entry, looking for one with the same PC offset which
     // has isForOp() set.
     for (size_t i = mid; i < numICEntries() && icEntry(i).pcOffset() == pcOffset; i--) {
         if (icEntry(i).isForOp())
             return icEntry(i);
     }
     for (size_t i = mid+1; i < numICEntries() && icEntry(i).pcOffset() == pcOffset; i++) {
@@ -688,17 +709,17 @@ BaselineScript::copyPCMappingEntries(con
 void
 BaselineScript::copyPCMappingIndexEntries(const PCMappingIndexEntry *entries)
 {
     for (uint32_t i = 0; i < numPCMappingIndexEntries(); i++)
         pcMappingIndexEntry(i) = entries[i];
 }
 
 uint8_t *
-BaselineScript::nativeCodeForPC(JSScript *script, jsbytecode *pc, PCMappingSlotInfo *slotInfo)
+BaselineScript::maybeNativeCodeForPC(JSScript *script, jsbytecode *pc, PCMappingSlotInfo *slotInfo)
 {
     MOZ_ASSERT_IF(script->hasBaselineScript(), script->baselineScript() == this);
 
     uint32_t pcOffset = script->pcToOffset(pc);
 
     // Look for the first PCMappingIndexEntry with pc > the pc we are
     // interested in.
     uint32_t i = 1;
@@ -716,31 +737,33 @@ BaselineScript::nativeCodeForPC(JSScript
 
     CompactBufferReader reader(pcMappingReader(i));
     jsbytecode *curPC = script->offsetToPC(entry.pcOffset);
     uint32_t nativeOffset = entry.nativeOffset;
 
     MOZ_ASSERT(script->containsPC(curPC));
     MOZ_ASSERT(curPC <= pc);
 
-    while (true) {
+    while (reader.more()) {
         // If the high bit is set, the native offset relative to the
         // previous pc != 0 and comes next.
         uint8_t b = reader.readByte();
         if (b & 0x80)
             nativeOffset += reader.readUnsigned();
 
         if (curPC == pc) {
             if (slotInfo)
                 *slotInfo = PCMappingSlotInfo(b & ~0x80);
             return method_->raw() + nativeOffset;
         }
 
         curPC += GetBytecodeLength(curPC);
     }
+
+    return nullptr;
 }
 
 jsbytecode *
 BaselineScript::pcForReturnOffset(JSScript *script, uint32_t nativeOffset)
 {
     return pcForNativeOffset(script, nativeOffset, true);
 }
 
@@ -787,16 +810,18 @@ BaselineScript::pcForNativeOffset(JSScri
     MOZ_ASSERT(script->containsPC(curPC));
     MOZ_ASSERT_IF(isReturn, nativeOffset >= curNativeOffset);
 
     // In the raw native-lookup case, the native code address can occur
     // before the start of ops.  Associate those with bytecode offset 0.
     if (!isReturn && (curNativeOffset > nativeOffset))
         return script->code();
 
+    mozilla::DebugOnly<uint32_t> lastNativeOffset = curNativeOffset;
+    jsbytecode *lastPC = curPC;
     while (true) {
         // If the high bit is set, the native offset relative to the
         // previous pc != 0 and comes next.
         uint8_t b = reader.readByte();
         if (b & 0x80)
             curNativeOffset += reader.readUnsigned();
 
         if (isReturn ? (nativeOffset == curNativeOffset) : (nativeOffset <= curNativeOffset))
--- a/js/src/jit/BaselineJIT.h
+++ b/js/src/jit/BaselineJIT.h
@@ -325,18 +325,18 @@ struct BaselineScript
 
     bool containsCodeAddress(uint8_t *addr) const {
         return method()->raw() <= addr && addr <= method()->raw() + method()->instructionsSize();
     }
 
     ICEntry &icEntry(size_t index);
     ICEntry *maybeICEntryFromReturnOffset(CodeOffsetLabel returnOffset);
     ICEntry &icEntryFromReturnOffset(CodeOffsetLabel returnOffset);
+    ICEntry &anyKindICEntryFromPCOffset(uint32_t pcOffset);
     ICEntry &icEntryFromPCOffset(uint32_t pcOffset);
-    ICEntry &icEntryForDebugModeRecompileFromPCOffset(uint32_t pcOffset);
     ICEntry &icEntryFromPCOffset(uint32_t pcOffset, ICEntry *prevLookedUpEntry);
     ICEntry *maybeICEntryFromReturnAddress(uint8_t *returnAddr);
     ICEntry &icEntryFromReturnAddress(uint8_t *returnAddr);
     uint8_t *returnAddressForIC(const ICEntry &ent);
 
     size_t numICEntries() const {
         return icEntries_;
     }
@@ -351,17 +351,25 @@ struct BaselineScript
 
     size_t numPCMappingIndexEntries() const {
         return pcMappingIndexEntries_;
     }
 
     void copyPCMappingIndexEntries(const PCMappingIndexEntry *entries);
 
     void copyPCMappingEntries(const CompactBufferWriter &entries);
-    uint8_t *nativeCodeForPC(JSScript *script, jsbytecode *pc, PCMappingSlotInfo *slotInfo = nullptr);
+    uint8_t *maybeNativeCodeForPC(JSScript *script, jsbytecode *pc,
+                                  PCMappingSlotInfo *slotInfo = nullptr);
+    uint8_t *nativeCodeForPC(JSScript *script, jsbytecode *pc,
+                             PCMappingSlotInfo *slotInfo = nullptr)
+    {
+        uint8_t *code = maybeNativeCodeForPC(script, pc, slotInfo);
+        MOZ_ASSERT(code);
+        return code;
+    }
 
     jsbytecode *pcForReturnOffset(JSScript *script, uint32_t nativeOffset);
     jsbytecode *pcForReturnAddress(JSScript *script, uint8_t *nativeAddress);
 
     jsbytecode *pcForNativeAddress(JSScript *script, uint8_t *nativeAddress);
     jsbytecode *pcForNativeOffset(JSScript *script, uint32_t nativeOffset);
 
     bool addDependentAsmJSModule(JSContext *cx, DependentAsmJSModuleExit exit);