Bug 857838 - Fix script-pc calculatins when iterating on baseline frames. r=jandem
authorKannan Vijayan <kvijayan@mozilla.com>
Mon, 22 Apr 2013 12:06:46 -0400
changeset 129488 6fc86d4607ac9adf87efbe63e6172e201404e753
parent 129487 ba1a51d840b384a33fa2729103a0befdacd2374a
child 129489 ddf944cc292d8d843089c37e83ec69b6d87f5d2c
push idunknown
push userunknown
push dateunknown
reviewersjandem
bugs857838
milestone23.0a1
Bug 857838 - Fix script-pc calculatins when iterating on baseline frames. r=jandem
js/src/ion/BaselineBailouts.cpp
js/src/ion/BaselineJIT.cpp
js/src/ion/BaselineJIT.h
js/src/ion/IonFrames.cpp
js/src/ion/IonMacroAssembler.cpp
--- a/js/src/ion/BaselineBailouts.cpp
+++ b/js/src/ion/BaselineBailouts.cpp
@@ -1179,16 +1179,23 @@ ion::FinishBailoutToBaseline(BaselineBai
 {
     // The caller pushes R0 and R1 on the stack without rooting them.
     // Since GC here is very unlikely just suppress it.
     JSContext *cx = GetIonContext()->cx;
     js::gc::AutoSuppressGC suppressGC(cx);
 
     IonSpew(IonSpew_BaselineBailouts, "  Done restoring frames");
 
+    // Check that we can get the current script's PC.
+#ifdef DEBUG
+    jsbytecode *pc;
+    cx->stack.currentScript(&pc);
+    IonSpew(IonSpew_BaselineBailouts, "  Got pc=%p", pc);
+#endif
+
     uint32_t numFrames = bailoutInfo->numFrames;
     JS_ASSERT(numFrames > 0);
     BailoutKind bailoutKind = bailoutInfo->bailoutKind;
 
     // Free the bailout buffer.
     js_free(bailoutInfo);
     bailoutInfo = NULL;
 
--- a/js/src/ion/BaselineJIT.cpp
+++ b/js/src/ion/BaselineJIT.cpp
@@ -383,32 +383,45 @@ BaselineScript::pcMappingReader(size_t i
     uint8_t *dataStart = pcMappingData() + entry.bufferOffset;
     uint8_t *dataEnd = (indexEntry == numPCMappingIndexEntries() - 1)
         ? pcMappingData() + pcMappingSize_
         : pcMappingData() + pcMappingIndexEntry(indexEntry + 1).bufferOffset;
 
     return CompactBufferReader(dataStart, dataEnd);
 }
 
-ICEntry &
-BaselineScript::icEntryFromReturnOffset(CodeOffsetLabel returnOffset)
+ICEntry *
+BaselineScript::maybeICEntryFromReturnOffset(CodeOffsetLabel returnOffset)
 {
     size_t bottom = 0;
     size_t top = numICEntries();
     size_t mid = (bottom + top) / 2;
     while (mid < top) {
         ICEntry &midEntry = icEntry(mid);
         if (midEntry.returnOffset().offset() < returnOffset.offset())
             bottom = mid + 1;
         else // if (midEntry.returnOffset().offset() >= returnOffset.offset())
             top = mid;
         mid = (bottom + top) / 2;
     }
-    JS_ASSERT(icEntry(mid).returnOffset().offset() == returnOffset.offset());
-    return icEntry(mid);
+    if (mid >= numICEntries())
+        return NULL;
+
+    if (icEntry(mid).returnOffset().offset() != returnOffset.offset())
+        return NULL;
+
+    return &icEntry(mid);
+}
+
+ICEntry &
+BaselineScript::icEntryFromReturnOffset(CodeOffsetLabel returnOffset)
+{
+    ICEntry *result = maybeICEntryFromReturnOffset(returnOffset);
+    JS_ASSERT(result);
+    return *result;
 }
 
 uint8_t *
 BaselineScript::returnAddressForIC(const ICEntry &ent)
 {
     return method()->raw() + ent.returnOffset().offset();
 }
 
@@ -463,20 +476,30 @@ BaselineScript::icEntryFromPCOffset(uint
         }
         JS_ASSERT(curEntry->pcOffset() == pcOffset && curEntry->isForOp());
         return *curEntry;
     }
 
     return icEntryFromPCOffset(pcOffset);
 }
 
+ICEntry *
+BaselineScript::maybeICEntryFromReturnAddress(uint8_t *returnAddr)
+{
+    JS_ASSERT(returnAddr > method_->raw());
+    JS_ASSERT(returnAddr < method_->raw() + method_->instructionsSize());
+    CodeOffsetLabel offset(returnAddr - method_->raw());
+    return maybeICEntryFromReturnOffset(offset);
+}
+
 ICEntry &
 BaselineScript::icEntryFromReturnAddress(uint8_t *returnAddr)
 {
     JS_ASSERT(returnAddr > method_->raw());
+    JS_ASSERT(returnAddr < method_->raw() + method_->instructionsSize());
     CodeOffsetLabel offset(returnAddr - method_->raw());
     return icEntryFromReturnOffset(offset);
 }
 
 void
 BaselineScript::copyICEntries(HandleScript script, const ICEntry *entries, MacroAssembler &masm)
 {
     // Fix up the return offset in the IC entries and copy them in.
@@ -576,16 +599,70 @@ BaselineScript::nativeCodeForPC(JSScript
 
         curPC += GetBytecodeLength(curPC);
     }
 
     JS_NOT_REACHED("Invalid pc");
     return NULL;
 }
 
+jsbytecode *
+BaselineScript::pcForReturnOffset(JSScript *script, uint32_t nativeOffset)
+{
+    JS_ASSERT(script->baselineScript() == this);
+    JS_ASSERT(nativeOffset < method_->instructionsSize());
+
+    // Look for the first PCMappingIndexEntry with native offset > the native offset we are
+    // interested in.
+    uint32_t i = 1;
+    for (; i < numPCMappingIndexEntries(); i++) {
+        if (pcMappingIndexEntry(i).nativeOffset > nativeOffset)
+            break;
+    }
+
+    // Go back an entry to search forward from.
+    JS_ASSERT(i > 0);
+    i--;
+
+    PCMappingIndexEntry &entry = pcMappingIndexEntry(i);
+    JS_ASSERT(nativeOffset >= entry.nativeOffset);
+
+    CompactBufferReader reader(pcMappingReader(i));
+    jsbytecode *curPC = script->code + entry.pcOffset;
+    uint32_t curNativeOffset = entry.nativeOffset;
+
+    JS_ASSERT(curPC >= script->code);
+    JS_ASSERT(curNativeOffset <= nativeOffset);
+
+    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 (curNativeOffset == nativeOffset)
+            return curPC;
+
+        curPC += GetBytecodeLength(curPC);
+    }
+
+    JS_NOT_REACHED("Invalid pc");
+    return NULL;
+}
+
+jsbytecode *
+BaselineScript::pcForReturnAddress(JSScript *script, uint8_t *nativeAddress)
+{
+    JS_ASSERT(script->baselineScript() == this);
+    JS_ASSERT(nativeAddress >= method_->raw());
+    JS_ASSERT(nativeAddress < method_->raw() + method_->instructionsSize());
+    return pcForReturnOffset(script, uint32_t(nativeAddress - method_->raw()));
+}
+
 void
 BaselineScript::toggleDebugTraps(RawScript script, jsbytecode *pc)
 {
     JS_ASSERT(script->baselineScript() == this);
 
     SrcNoteLineScanner scanner(script->notes(), script->lineno);
 
     IonContext ictx(script->compartment(), NULL);
--- a/js/src/ion/BaselineJIT.h
+++ b/js/src/ion/BaselineJIT.h
@@ -206,19 +206,21 @@ struct BaselineScript
         method_ = code;
     }
 
     void toggleBarriers(bool enabled) {
         method()->togglePreBarriers(enabled);
     }
 
     ICEntry &icEntry(size_t index);
+    ICEntry *maybeICEntryFromReturnOffset(CodeOffsetLabel returnOffset);
     ICEntry &icEntryFromReturnOffset(CodeOffsetLabel returnOffset);
     ICEntry &icEntryFromPCOffset(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_;
     }
 
     void copyICEntries(HandleScript script, const ICEntry *entries, MacroAssembler &masm);
@@ -230,16 +232,18 @@ 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 = NULL);
+    jsbytecode *pcForReturnOffset(JSScript *script, uint32_t nativeOffset);
+    jsbytecode *pcForReturnAddress(JSScript *script, uint8_t *nativeAddress);
 
     // Toggle debug traps (used for breakpoints and step mode) in the script.
     // If |pc| is NULL, toggle traps for all ops in the script. Else, only
     // toggle traps at |pc|.
     void toggleDebugTraps(RawScript script, jsbytecode *pc);
 
     void toggleSPS(bool enable);
 
--- a/js/src/ion/IonFrames.cpp
+++ b/js/src/ion/IonFrames.cpp
@@ -188,18 +188,35 @@ IonFrameIterator::script() const
 void
 IonFrameIterator::baselineScriptAndPc(JSScript **scriptRes, jsbytecode **pcRes) const
 {
     JS_ASSERT(isBaselineJS());
     RawScript script = this->script();
     if (scriptRes)
         *scriptRes = script;
     uint8_t *retAddr = returnAddressToFp();
-    if (pcRes)
-        *pcRes = script->baselineScript()->icEntryFromReturnAddress(retAddr).pc(script);
+    if (pcRes) {
+        // If the return address is into the prologue entry addr, then assume PC 0.
+        if (retAddr == script->baselineScript()->prologueEntryAddr()) {
+            *pcRes = 0;
+            return;
+        }
+
+        // The return address _may_ be a return from a callVM or IC chain call done for
+        // some op.
+        ICEntry *icEntry = script->baselineScript()->maybeICEntryFromReturnAddress(retAddr);
+        if (icEntry) {
+            *pcRes = icEntry->pc(script);
+            return;
+        }
+
+        // If not, the return address _must_ be the start address of an op, which can
+        // be computed from the pc mapping table.
+        *pcRes = script->baselineScript()->pcForReturnAddress(script, retAddr);
+    }
 }
 
 Value *
 IonFrameIterator::nativeVp() const
 {
     JS_ASSERT(isNative());
     return exitFrame()->nativeExit()->vp();
 }
--- a/js/src/ion/IonMacroAssembler.cpp
+++ b/js/src/ion/IonMacroAssembler.cpp
@@ -842,17 +842,18 @@ MacroAssembler::generateBailoutTail(Regi
             bind(&endOfCopy);
         }
 
         // Enter exit frame for the FinishBailoutToBaseline call.
         loadPtr(Address(bailoutInfo, offsetof(BaselineBailoutInfo, resumeFramePtr)), temp);
         load32(Address(temp, BaselineFrame::reverseOffsetOfFrameSize()), temp);
         makeFrameDescriptor(temp, IonFrame_BaselineJS);
         push(temp);
-        push(Imm32(0)); // Fake return address.
+        loadPtr(Address(bailoutInfo, offsetof(BaselineBailoutInfo, resumeAddr)), temp);
+        push(temp);
         enterFakeExitFrame();
 
         // If monitorStub is non-null, handle resumeAddr appropriately.
         Label noMonitor;
         Label done;
         branchPtr(Assembler::Equal,
                   Address(bailoutInfo, offsetof(BaselineBailoutInfo, monitorStub)),
                   ImmWord((void*) 0),