Bug 823482 part 2 - Compile try-catch. r=djvj
authorJan de Mooij <jdemooij@mozilla.com>
Mon, 31 Dec 2012 13:50:54 +0100
changeset 117375 e5b05b7abbcc9b2f95f0687e321177695251c418
parent 117374 dd088ae5e3ef1b795963defc744b3847bf0559b4
child 117376 fd85293b49e211f24f24177068d8b1ce664211ac
push id1426
push userjandemooij@gmail.com
push dateMon, 31 Dec 2012 12:53:23 +0000
reviewersdjvj
bugs823482
milestone20.0a1
Bug 823482 part 2 - Compile try-catch. r=djvj
js/src/ion/BaselineCompiler.cpp
js/src/ion/BaselineCompiler.h
js/src/ion/BaselineFrameInfo.h
js/src/ion/BaselineJIT.cpp
js/src/ion/BaselineJIT.h
js/src/ion/IonFrames.cpp
js/src/ion/IonFrames.h
js/src/ion/arm/MacroAssembler-arm.cpp
js/src/ion/shared/BaselineCompiler-shared.cpp
js/src/ion/shared/BaselineCompiler-shared.h
js/src/ion/x64/MacroAssembler-x64.cpp
js/src/ion/x86/MacroAssembler-x86.cpp
--- a/js/src/ion/BaselineCompiler.cpp
+++ b/js/src/ion/BaselineCompiler.cpp
@@ -74,31 +74,34 @@ BaselineCompiler::compile()
 
     Linker linker(masm);
     IonCode *code = linker.newCode(cx);
     if (!code)
         return Method_Error;
 
     JS_ASSERT(!script->hasBaselineScript());
 
-    BaselineScript *baselineScript = BaselineScript::New(cx, icEntries_.length());
+    BaselineScript *baselineScript = BaselineScript::New(cx, icEntries_.length(), pcMappingEntries_.length());
     if (!baselineScript)
         return Method_Error;
     script->baseline = baselineScript;
 
     IonSpew(IonSpew_BaselineScripts, "Created BaselineScript %p (raw %p) for %s:%d",
             (void *) script->baseline, (void *) code->raw(),
             script->filename, script->lineno);
 
     script->baseline->setMethod(code);
 
     // Copy IC entries
     if (icEntries_.length())
         baselineScript->copyICEntries(&icEntries_[0], masm);
 
+    if (pcMappingEntries_.length())
+        baselineScript->copyPCMappingEntries(&pcMappingEntries_[0]);
+
     // Adopt fallback stubs from the compiler into the baseline script.
     baselineScript->adoptFallbackStubs(&stubSpace_);
 
     // Patch IC loads using IC entries
     for (size_t i = 0; i < icLoadLabels_.length(); i++) {
         CodeOffsetLabel label = icLoadLabels_[i].label;
         label.fixup(&masm);
         size_t icEntry = icLoadLabels_[i].icEntry;
@@ -1290,16 +1293,68 @@ BaselineCompiler::emit_JSOP_THROW()
 
     prepareVMCall();
     pushArg(R0);
 
     return callVM(ThrowInfo);
 }
 
 bool
+BaselineCompiler::emit_JSOP_TRY()
+{
+    return true;
+}
+
+bool
+BaselineCompiler::emit_JSOP_ENTERBLOCK()
+{
+    // ENTERBLOCK is emitted at the start of catch blocks. Record the native
+    // code offset so that the exception handler can jump here.
+    if (!addPCMappingEntry())
+        return false;
+
+    // TODO: clone block if needed, push on block chain.
+    StaticBlockObject &blockObj = script->getObject(pc)->asStaticBlock();
+
+    for (size_t i = 0; i < blockObj.slotCount(); i++)
+        frame.push(UndefinedValue());
+
+    // Pushed values will be accessed using GETLOCAL and SETLOCAL, so ensure
+    // they are synced.
+    frame.syncStack(0);
+    return true;
+}
+
+bool
+BaselineCompiler::emit_JSOP_LEAVEBLOCK()
+{
+    // TODO: pop block from block chain.
+
+    size_t n = StackUses(script, pc);
+    frame.popn(n);
+    return true;
+}
+
+typedef bool (*GetAndClearExceptionFn)(JSContext *, MutableHandleValue);
+static const VMFunction GetAndClearExceptionInfo =
+    FunctionInfo<GetAndClearExceptionFn>(GetAndClearException);
+
+bool
+BaselineCompiler::emit_JSOP_EXCEPTION()
+{
+    prepareVMCall();
+
+    if (!callVM(GetAndClearExceptionInfo))
+        return false;
+
+    frame.push(R0);
+    return true;
+}
+
+bool
 BaselineCompiler::emit_JSOP_RETURN()
 {
     JS_ASSERT(frame.stackDepth() == 1);
 
     frame.popValue(JSReturnOperand);
     masm.jump(return_);
     return true;
 }
--- a/js/src/ion/BaselineCompiler.h
+++ b/js/src/ion/BaselineCompiler.h
@@ -106,16 +106,20 @@ namespace ion {
     _(JSOP_GETARG)             \
     _(JSOP_CALLARG)            \
     _(JSOP_SETARG)             \
     _(JSOP_CALL)               \
     _(JSOP_FUNCALL)            \
     _(JSOP_FUNAPPLY)           \
     _(JSOP_NEW)                \
     _(JSOP_THROW)              \
+    _(JSOP_TRY)                \
+    _(JSOP_ENTERBLOCK)         \
+    _(JSOP_LEAVEBLOCK)         \
+    _(JSOP_EXCEPTION)          \
     _(JSOP_RETURN)             \
     _(JSOP_STOP)
 
 class BaselineCompiler : public BaselineCompilerSpecific
 {
     FixedList<Label>            labels_;
     HeapLabel *                 return_;
 
--- a/js/src/ion/BaselineFrameInfo.h
+++ b/js/src/ion/BaselineFrameInfo.h
@@ -167,18 +167,27 @@ class FrameInfo
         return val;
     }
 
   public:
     inline size_t stackDepth() const {
         return spIndex;
     }
     inline void setStackDepth(uint32_t newDepth) {
-        JS_ASSERT(newDepth <= stackDepth());
-        spIndex = newDepth;
+        if (newDepth <= stackDepth()) {
+            spIndex = newDepth;
+        } else {
+            uint32_t diff = newDepth - stackDepth();
+            for (uint32_t i = 0; i < diff; i++) {
+                StackValue *val = rawPush();
+                val->setStack();
+            }
+
+            JS_ASSERT(spIndex == newDepth);
+        }
     }
     inline StackValue *peek(int32_t index) const {
         JS_ASSERT(index < 0);
         return const_cast<StackValue *>(&stack[spIndex + index]);
     }
 
     inline void pop(StackAdjustment adjust = AdjustStack) {
         spIndex--;
@@ -221,17 +230,25 @@ class FrameInfo
         sv->setThis();
     }
     inline void pushScratchValue() {
         masm.pushValue(addressOfScratchValue());
         StackValue *sv = rawPush();
         sv->setStack();
     }
     inline Address addressOfLocal(size_t local) const {
-        JS_ASSERT(local < nlocals());
+#ifdef DEBUG
+        if (local >= nlocals()) {
+            // GETLOCAL and SETLOCAL can be used to access stack values. This is
+            // fine, as long as they are synced.
+            size_t slot = local - nlocals();
+            JS_ASSERT(slot < stackDepth());
+            JS_ASSERT(stack[slot].kind() == StackValue::Stack);
+        }
+#endif
         return Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfLocal(local));
     }
     inline Address addressOfArg(size_t arg) const {
         JS_ASSERT(arg < nargs());
         return Address(BaselineFrameReg, BaselineFrame::offsetOfArg(arg));
     }
     inline Address addressOfThis() const {
         return Address(BaselineFrameReg, BaselineFrame::offsetOfThis());
@@ -253,16 +270,20 @@ class FrameInfo
     }
 
     void popValue(ValueOperand dest);
 
     void sync(StackValue *val);
     void syncStack(uint32_t uses);
     void popRegsAndSync(uint32_t uses);
 
+    inline void assertSyncedStack() const {
+        JS_ASSERT_IF(stackDepth() > 0, peek(-1)->kind() == StackValue::Stack);
+    }
+
 #ifdef DEBUG
     // Assert the state is valid before excuting "pc".
     void assertValidState(jsbytecode *pc);
 #else
     inline void assertValidState(jsbytecode *pc) {}
 #endif
 };
 
--- a/js/src/ion/BaselineJIT.cpp
+++ b/js/src/ion/BaselineJIT.cpp
@@ -237,36 +237,46 @@ ion::CanEnterBaselineJIT(JSContext *cx, 
 
     return BaselineCompile(cx, script, fp);
 }
 
 // Be safe, align IC entry list to 8 in all cases.
 static const unsigned DataAlignment = sizeof(uintptr_t);
 
 BaselineScript *
-BaselineScript::New(JSContext *cx, size_t icEntries)
+BaselineScript::New(JSContext *cx, size_t icEntries, size_t pcMappingEntries)
 {
     size_t paddedBaselineScriptSize = AlignBytes(sizeof(BaselineScript), DataAlignment);
 
     size_t icEntriesSize = icEntries * sizeof(ICEntry);
-    size_t paddedICEntriesSize = AlignBytes(icEntriesSize, DataAlignment);
+    size_t pcMappingEntriesSize = pcMappingEntries * sizeof(PCMappingEntry);
 
-    size_t allocBytes = paddedBaselineScriptSize + paddedICEntriesSize;
+    size_t paddedICEntriesSize = AlignBytes(icEntriesSize, DataAlignment);
+    size_t paddedPCMappingEntriesSize = AlignBytes(pcMappingEntriesSize, DataAlignment);
+
+    size_t allocBytes = paddedBaselineScriptSize +
+        paddedICEntriesSize +
+        paddedPCMappingEntriesSize;
 
     uint8_t *buffer = (uint8_t *)cx->malloc_(allocBytes);
     if (!buffer)
         return NULL;
 
     BaselineScript *script = reinterpret_cast<BaselineScript *>(buffer);
     new (script) BaselineScript();
 
-    uint8_t *icEntryStart = buffer + paddedBaselineScriptSize;
+    size_t offsetCursor = paddedBaselineScriptSize;
 
-    script->icEntriesOffset_ = (uint32_t) (icEntryStart - buffer);
+    script->icEntriesOffset_ = offsetCursor;
     script->icEntries_ = icEntries;
+    offsetCursor += paddedICEntriesSize;
+
+    script->pcMappingOffset_ = offsetCursor;
+    script->pcMappingEntries_ = pcMappingEntries;
+    offsetCursor += paddedPCMappingEntriesSize;
 
     return script;
 }
 
 void
 BaselineScript::trace(JSTracer *trc)
 {
     MarkIonCode(trc, &method_, "baseline-method");
@@ -338,8 +348,39 @@ BaselineScript::copyICEntries(const ICEn
     }
 }
 
 void
 BaselineScript::adoptFallbackStubs(ICStubSpace *stubSpace)
 {
     fallbackStubSpace_.adoptFrom(stubSpace);
 }
+
+PCMappingEntry &
+BaselineScript::pcMappingEntry(size_t index)
+{
+    JS_ASSERT(index < numPCMappingEntries());
+    return pcMappingEntryList()[index];
+}
+
+void
+BaselineScript::copyPCMappingEntries(const PCMappingEntry *entries)
+{
+    for (uint32_t i = 0; i < numPCMappingEntries(); i++)
+        pcMappingEntry(i) = entries[i];
+}
+
+uint8_t *
+BaselineScript::nativeCodeForPC(HandleScript script, jsbytecode *pc)
+{
+    JS_ASSERT(script->baseline == this);
+
+    uint32_t pcOffset = pc - script->code;
+
+    for (size_t i = 0; i < numPCMappingEntries(); i++) {
+        PCMappingEntry &entry = pcMappingEntry(i);
+        if (entry.pcOffset == pcOffset)
+            return method_->raw() + entry.nativeOffset;
+    }
+
+    JS_NOT_REACHED("Invalid pc");
+    return NULL;
+}
--- a/js/src/ion/BaselineJIT.h
+++ b/js/src/ion/BaselineJIT.h
@@ -41,16 +41,23 @@ struct ICStubSpace
     inline void adoptFrom(ICStubSpace *other) {
         allocator_.transferFrom(&(other->allocator_));
     }
 
     static ICStubSpace *FallbackStubSpaceFor(JSScript *script);
     static ICStubSpace *StubSpaceFor(JSScript *script);
 };
 
+// Stores the native code offset for a bytecode pc.
+struct PCMappingEntry
+{
+    uint32_t pcOffset;
+    uint32_t nativeOffset;
+};
+
 struct BaselineScript
 {
   private:
     // Code pointer containing the actual method.
     HeapPtr<IonCode> method_;
 
     // Allocated space for fallback stubs.
     ICStubSpace fallbackStubSpace_;
@@ -59,31 +66,37 @@ struct BaselineScript
     ICStubSpace optimizedStubSpace_;
 
   private:
     void trace(JSTracer *trc);
 
     uint32_t icEntriesOffset_;
     uint32_t icEntries_;
 
+    uint32_t pcMappingOffset_;
+    uint32_t pcMappingEntries_;
+
   public:
     // Do not call directly, use BaselineScript::New. This is public for cx->new_.
     BaselineScript();
 
-    static BaselineScript *New(JSContext *cx, size_t icEntries);
+    static BaselineScript *New(JSContext *cx, size_t icEntries, size_t pcMappingEntries);
     static void Trace(JSTracer *trc, BaselineScript *script);
     static void Destroy(FreeOp *fop, BaselineScript *script);
 
     static inline size_t offsetOfMethod() {
         return offsetof(BaselineScript, method_);
     }
 
     ICEntry *icEntryList() {
         return (ICEntry *)(reinterpret_cast<uint8_t *>(this) + icEntriesOffset_);
     }
+    PCMappingEntry *pcMappingEntryList() {
+        return (PCMappingEntry *)(reinterpret_cast<uint8_t *>(this) + pcMappingOffset_);
+    }
 
     ICStubSpace *fallbackStubSpace() {
         return &fallbackStubSpace_;
     }
 
     ICStubSpace *optimizedStubSpace() {
         return &optimizedStubSpace_;
     }
@@ -101,16 +114,24 @@ struct BaselineScript
     ICEntry &icEntryFromReturnAddress(uint8_t *returnAddr);
 
     size_t numICEntries() const {
         return icEntries_;
     }
 
     void copyICEntries(const ICEntry *entries, MacroAssembler &masm);
     void adoptFallbackStubs(ICStubSpace *stubSpace);
+
+    size_t numPCMappingEntries() const {
+        return pcMappingEntries_;
+    }
+
+    PCMappingEntry &pcMappingEntry(size_t index);
+    void copyPCMappingEntries(const PCMappingEntry *entries);
+    uint8_t *nativeCodeForPC(HandleScript script, jsbytecode *pc);
 };
 
 inline bool IsBaselineEnabled(JSContext *cx)
 {
     return cx->hasRunOption(JSOPTION_BASELINE);
 }
 
 MethodStatus
--- a/js/src/ion/IonFrames.cpp
+++ b/js/src/ion/IonFrames.cpp
@@ -314,28 +314,80 @@ CloseLiveIterators(JSContext *cx, const 
         JS_ASSERT(JSOp(*(script->main() + tn->start + tn->length)) == JSOP_ENDITER);
         JS_ASSERT(tn->stackDepth > 0);
 
         uint32_t localSlot = tn->stackDepth;
         CloseLiveIterator(cx, frame, localSlot);
     }
 }
 
+static void
+HandleException(JSContext *cx, const IonFrameIterator &frame, ResumeFromException *rfe)
+{
+    JS_ASSERT(frame.isBaselineJS());
+    AssertCanGC();
+
+    RootedScript script(cx);
+    jsbytecode *pc;
+    frame.baselineScriptAndPc(&script, &pc);
+
+    if (!script->hasTrynotes())
+        return;
+
+    JSTryNote *tn = script->trynotes()->vector;
+    JSTryNote *tnEnd = tn + script->trynotes()->length;
+
+    uint32_t pcOffset = uint32_t(pc - script->main());
+    for (; tn != tnEnd; ++tn) {
+        if (pcOffset < tn->start)
+            continue;
+        if (pcOffset >= tn->start + tn->length)
+            continue;
+
+        // Compute base pointer and stack pointer.
+        rfe->framePointer = frame.fp() - BaselineFrame::FramePointerOffset;
+        rfe->stackPointer = rfe->framePointer - BaselineFrame::Size() -
+            (script->nfixed + tn->stackDepth) * sizeof(Value);
+
+        switch (tn->kind) {
+          case JSTRY_CATCH: {
+            // Resume at the start of the catch block.
+            rfe->kind = ResumeFromException::RESUME_CATCH;
+            jsbytecode *catchPC = script->main() + tn->start + tn->length;
+            rfe->target = script->baseline->nativeCodeForPC(script, catchPC);
+            return;
+          }
+          default:
+            JS_NOT_REACHED("Invalid try note");
+        }
+    }
+
+}
+
 void
 ion::HandleException(ResumeFromException *rfe)
 {
     AssertCanGC();
     JSContext *cx = GetIonContext()->cx;
 
+    rfe->kind = ResumeFromException::RESUME_ENTRY_FRAME;
+
     IonSpew(IonSpew_Invalidate, "handling exception");
 
     // Immediately remove any bailout frame guard that might be left over from
     // an error in between ConvertFrames and ThunkToInterpreter.
     js_delete(cx->runtime->ionActivation->maybeTakeBailout());
 
+    // Clear any Ion return override that's been set.
+    // This may happen if a callVM function causes an invalidation (setting the
+    // override), and then fails, bypassing the bailout handlers that would
+    // otherwise clear the return override.
+    if (cx->runtime->hasIonReturnOverride())
+        cx->runtime->takeIonReturnOverride();
+
     IonFrameIterator iter(cx->runtime->ionTop);
     while (!iter.isEntry()) {
         if (iter.isOptimizedJS()) {
             // Search each inlined frame for live iterator objects, and close
             // them.
             InlineFrameIterator frames(&iter);
             for (;;) {
                 CloseLiveIterators(cx, frames);
@@ -351,26 +403,25 @@ ion::HandleException(ResumeFromException
                 ++frames;
             }
 
             IonScript *ionScript;
             if (iter.checkInvalidation(&ionScript))
                 ionScript->decref(cx->runtime->defaultFreeOp());
         }
 
+        if (iter.isBaselineJS()) {
+            HandleException(cx, iter, rfe);
+            if (rfe->kind == ResumeFromException::RESUME_CATCH)
+                return;
+        }
+
         ++iter;
     }
 
-    // Clear any Ion return override that's been set.
-    // This may happen if a callVM function causes an invalidation (setting the
-    // override), and then fails, bypassing the bailout handlers that would
-    // otherwise clear the return override.
-    if (cx->runtime->hasIonReturnOverride())
-        cx->runtime->takeIonReturnOverride();
-
     rfe->stackPointer = iter.fp();
 }
 
 void
 IonActivationIterator::settle()
 {
     while (activation_ && activation_->empty()) {
         top_ = activation_->prevIonTop();
--- a/js/src/ion/IonFrames.h
+++ b/js/src/ion/IonFrames.h
@@ -238,17 +238,23 @@ class FrameSizeClass
     bool operator !=(const FrameSizeClass &other) const {
         return class_ != other.class_;
     }
 };
 
 // Data needed to recover from an exception.
 struct ResumeFromException
 {
-    void *stackPointer;
+    static const uint32_t RESUME_ENTRY_FRAME = 0;
+    static const uint32_t RESUME_CATCH = 1;
+
+    uint8_t *framePointer;
+    uint8_t *stackPointer;
+    uint8_t *target;
+    uint32_t kind;
 };
 
 void HandleException(ResumeFromException *rfe);
 
 void MarkIonActivations(JSRuntime *rt, JSTracer *trc);
 void MarkIonCompilerRoots(JSTracer *trc);
 
 static inline uint32_t
--- a/js/src/ion/arm/MacroAssembler-arm.cpp
+++ b/js/src/ion/arm/MacroAssembler-arm.cpp
@@ -2957,24 +2957,43 @@ MacroAssemblerARMCompat::handleException
     int size = (sizeof(ResumeFromException) + 7) & ~7;
     ma_sub(Imm32(size), sp);
     ma_mov(sp, r0);
 
     // Ask for an exception handler.
     setupUnalignedABICall(1, r1);
     passABIArg(r0);
     callWithABI(JS_FUNC_TO_DATA_PTR(void *, ion::HandleException));
-    // Load the error value, load the new stack pointer, and return.
+
+    Label catch_;
+    Label entryFrame;
+
+    ma_ldr(Operand(sp, offsetof(ResumeFromException, kind)), r0);
+    branch32(Assembler::Equal, r0, Imm32(ResumeFromException::RESUME_ENTRY_FRAME), &entryFrame);
+    branch32(Assembler::Equal, r0, Imm32(ResumeFromException::RESUME_CATCH), &catch_);
+
+    breakpoint(); // Invalid kind.
+
+    // No exception handler. Load the error value, load the new stack pointer
+    // and return from the entry frame.
+    bind(&entryFrame);
     moveValue(MagicValue(JS_ION_ERROR), JSReturnOperand);
     ma_ldr(Operand(sp, offsetof(ResumeFromException, stackPointer)), sp);
 
     // We're going to be returning by the ion calling convention, which returns
     // by ??? (for now, I think ldr pc, [sp]!)
     as_dtr(IsLoad, 32, PostIndex, pc, DTRAddr(sp, DtrOffImm(4)));
 
+    // If we found a catch handler, this must be a baseline frame. Restore state
+    // and jump to the catch block.
+    bind(&catch_);
+    ma_ldr(Operand(sp, offsetof(ResumeFromException, target)), r0);
+    ma_ldr(Operand(sp, offsetof(ResumeFromException, framePointer)), r11);
+    ma_ldr(Operand(sp, offsetof(ResumeFromException, stackPointer)), sp);
+    jump(r0);
 }
 
 Assembler::Condition
 MacroAssemblerARMCompat::testStringTruthy(bool truthy, const ValueOperand &value)
 {
     Register string = value.payloadReg();
 
     size_t mask = (0xFFFFFFFF << JSString::LENGTH_SHIFT);
--- a/js/src/ion/shared/BaselineCompiler-shared.cpp
+++ b/js/src/ion/shared/BaselineCompiler-shared.cpp
@@ -45,17 +45,17 @@ BaselineCompilerShared::callVM(const VMF
     uint32_t argSize = fun.explicitStackSlots() * sizeof(void *) + sizeof(void *);
 
     // Assert all arguments were pushed.
     JS_ASSERT(masm.framePushed() - pushedBeforeCall_ == argSize);
 
     uint32_t frameSize = BaselineFrame::FramePointerOffset + BaselineFrame::Size() +
         (frame.nlocals() + frame.stackDepth()) * sizeof(Value);
 
-    masm.store32(Imm32(frameSize), Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfFrameSize()));
+    masm.storePtr(ImmWord(frameSize), Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfFrameSize()));
 
     uint32_t descriptor = MakeFrameDescriptor(frameSize + argSize, IonFrame_BaselineJS);
     masm.push(Imm32(descriptor));
 
     // Perform the call.
     masm.call(code);
     uint32_t callOffset = masm.currentOffset();
     masm.pop(BaselineFrameReg);
--- a/js/src/ion/shared/BaselineCompiler-shared.h
+++ b/js/src/ion/shared/BaselineCompiler-shared.h
@@ -25,16 +25,18 @@ class BaselineCompilerShared
     MacroAssembler masm;
     bool ionCompileable_;
 
     FrameInfo frame;
 
     ICStubSpace stubSpace_;
     js::Vector<ICEntry, 16, SystemAllocPolicy> icEntries_;
 
+    js::Vector<PCMappingEntry, 16, SystemAllocPolicy> pcMappingEntries_;
+
     // Labels for the 'movWithPatch' for loading IC entry pointers in
     // the generated IC-calling code in the main jitcode.  These need
     // to be patched with the actual icEntry offsets after the BaselineScript
     // has been allocated.
     struct ICLoadLabel {
         size_t icEntry;
         CodeOffsetLabel label;
     };
@@ -69,16 +71,28 @@ class BaselineCompilerShared
         loadLabel.icEntry = icEntries_.length() - 1;
         return icLoadLabels_.append(loadLabel);
     }
 
     JSFunction *function() const {
         return script->function();
     }
 
+    bool addPCMappingEntry() {
+        frame.assertSyncedStack();
+
+        masm.flushBuffer();
+
+        PCMappingEntry entry;
+        entry.pcOffset = pc - script->code;
+        entry.nativeOffset = masm.currentOffset();
+
+        return pcMappingEntries_.append(entry);
+    }
+
     template <typename T>
     void pushArg(const T& t) {
         masm.Push(t);
     }
     void prepareVMCall() {
         pushedBeforeCall_ = masm.framePushed();
         inCall_ = true;
 
--- a/js/src/ion/x64/MacroAssembler-x64.cpp
+++ b/js/src/ion/x64/MacroAssembler-x64.cpp
@@ -186,20 +186,40 @@ MacroAssemblerX64::handleException()
     subq(Imm32(sizeof(ResumeFromException)), rsp);
     movq(rsp, rax);
 
     // Ask for an exception handler.
     setupUnalignedABICall(1, rcx);
     passABIArg(rax);
     callWithABI(JS_FUNC_TO_DATA_PTR(void *, ion::HandleException));
 
-    // Load the error value, load the new stack pointer, and return.
+    Label catch_;
+    Label entryFrame;
+
+    branch32(Assembler::Equal, Address(rsp, offsetof(ResumeFromException, kind)),
+             Imm32(ResumeFromException::RESUME_ENTRY_FRAME), &entryFrame);
+    branch32(Assembler::Equal, Address(rsp, offsetof(ResumeFromException, kind)),
+             Imm32(ResumeFromException::RESUME_CATCH), &catch_);
+
+    breakpoint(); // Invalid kind.
+
+    // No exception handler. Load the error value, load the new stack pointer
+    // and return from the entry frame.
+    bind(&entryFrame);
     moveValue(MagicValue(JS_ION_ERROR), JSReturnOperand);
     movq(Operand(rsp, offsetof(ResumeFromException, stackPointer)), rsp);
     ret();
+
+    // If we found a catch handler, this must be a baseline frame. Restore state
+    // and jump to the catch block.
+    bind(&catch_);
+    movq(Operand(rsp, offsetof(ResumeFromException, target)), rax);
+    movq(Operand(rsp, offsetof(ResumeFromException, basePointer)), rbp);
+    movq(Operand(rsp, offsetof(ResumeFromException, stackPointer)), rsp);
+    jmp(Operand(rax));
 }
 
 Assembler::Condition
 MacroAssemblerX64::testNegativeZero(const FloatRegister &reg, const Register &scratch)
 {
     movqsd(reg, scratch);
     subq(Imm32(1), scratch);
     return Overflow;
--- a/js/src/ion/x86/MacroAssembler-x86.cpp
+++ b/js/src/ion/x86/MacroAssembler-x86.cpp
@@ -152,21 +152,41 @@ MacroAssemblerX86::handleException()
     // Reserve space for exception information.
     subl(Imm32(sizeof(ResumeFromException)), esp);
     movl(esp, eax);
 
     // Ask for an exception handler.
     setupUnalignedABICall(1, ecx);
     passABIArg(eax);
     callWithABI(JS_FUNC_TO_DATA_PTR(void *, ion::HandleException));
-    
-    // Load the error value, load the new stack pointer, and return.
+
+    Label catch_;
+    Label entryFrame;
+
+    branch32(Assembler::Equal, Address(esp, offsetof(ResumeFromException, kind)),
+             Imm32(ResumeFromException::RESUME_ENTRY_FRAME), &entryFrame);
+    branch32(Assembler::Equal, Address(esp, offsetof(ResumeFromException, kind)),
+             Imm32(ResumeFromException::RESUME_CATCH), &catch_);
+
+    breakpoint(); // Invalid kind.
+
+    // No exception handler. Load the error value, load the new stack pointer
+    // and return from the entry frame.
+    bind(&entryFrame);
     moveValue(MagicValue(JS_ION_ERROR), JSReturnOperand);
     movl(Operand(esp, offsetof(ResumeFromException, stackPointer)), esp);
     ret();
+
+    // If we found a catch handler, this must be a baseline frame. Restore state
+    // and jump to the catch block.
+    bind(&catch_);
+    movl(Operand(esp, offsetof(ResumeFromException, target)), eax);
+    movl(Operand(esp, offsetof(ResumeFromException, framePointer)), ebp);
+    movl(Operand(esp, offsetof(ResumeFromException, stackPointer)), esp);
+    jmp(Operand(eax));
 }
 
 void
 MacroAssemblerX86::branchTestValue(Condition cond, const ValueOperand &value, const Value &v, Label *label)
 {
     jsval_layout jv = JSVAL_TO_IMPL(v);
     if (v.isMarkable())
         cmpl(value.payloadReg(), ImmGCPtr(reinterpret_cast<gc::Cell *>(v.toGCThing())));