FTL B3 should support OSR exit
https://bugs.webkit.org/show_bug.cgi?id=151710

Reviewed by Saam Barati.

Source/JavaScriptCore:

This adds OSR exit support using the same style that I established with lazy slow paths. All of
the work is driven by FTL::LowerDFGToLLVM, and from there any work that needs to be deferred
until after B3 finishes is attached to the stackmap generator. In order to make it easy to port
all of the different forms of OSR exit - invalidation points, exceptions, etc. - the logic for
registering an OSR exit is abstracted behind OSRExitDescriptor and OSRExitHandle.

An issue that I encountered repeatedly in this patch is OSRExitDescriptor being passed as a
reference (&) rather than pointer (*). The new code uses a lot of lambdas that run after the
current frame pops, so the capture list cannot be [&]. I believe that always listing all of the
captured variables is not scalable considering how sophisticated our use of lambdas is. So, it
makes sense to use [=]. But anytime we captured a variable whose type was OSRExitDescriptor&, it
would be captured by value, because that's how references work. One has to be mindful of these
things whenever using [=]. Note that it's not enough to say that we should have listed the
captured variables explicitly - in that case, we still could have made the mistake by forgetting
to put & in front of the variant. The pattern that worked for me to reason about whether I'm
capturing an object or a pointer to an object is to always use pointer types for pointers: either
RefPtr<> when we also want the lambda to prolong the object's life, or * if we are confident that
the object will stay alive. For this reason, this patch changes all code that references
OSRExitDescriptor to use * instead of &. Consistency makes the code easier to grok, and it made
it easier to introduce the required uses of * in places where there were lambdas.

I tested this by running imaging-gaussian-blur, and running some tests that reqiure OSR exit. I'm
not promising that all kinds of exits work, but we have to begin somewhere.

* CMakeLists.txt:
* JavaScriptCore.xcodeproj/project.pbxproj:
* b3/B3Compilation.cpp:
(JSC::B3::Compilation::Compilation):
(JSC::B3::Compilation::~Compilation):
* b3/B3Procedure.cpp:
(JSC::B3::Procedure::addDataSection):
(JSC::B3::Procedure::frameSize):
(JSC::B3::Procedure::calleeSaveRegisters):
* b3/B3Procedure.h:
(JSC::B3::Procedure::releaseByproducts):
(JSC::B3::Procedure::code):
(JSC::B3::Procedure::takeByproducts): Deleted.
* b3/air/AirCode.h:
(JSC::B3::Air::Code::setFrameSize):
(JSC::B3::Air::Code::calleeSaveRegisters):
* b3/air/AirGenerationContext.h:
* ftl/FTLB3Compile.cpp:
(JSC::FTL::compile):
* ftl/FTLCompile.cpp:
(JSC::FTL::mmAllocateDataSection):
* ftl/FTLExceptionHandlerManager.cpp:
(JSC::FTL::ExceptionHandlerManager::lazySlowPathExceptionTarget):
(JSC::FTL::ExceptionHandlerManager::getCallOSRExitCommon):
* ftl/FTLExitThunkGenerator.cpp:
* ftl/FTLExitThunkGenerator.h:
* ftl/FTLJITCode.cpp:
(JSC::FTL::JITCode::JITCode):
(JSC::FTL::JITCode::initializeB3Code):
(JSC::FTL::JITCode::initializeB3Byproducts):
(JSC::FTL::JITCode::initializeExitThunks):
(JSC::FTL::JITCode::validateReferences):
(JSC::FTL::JITCode::liveRegistersToPreserveAtExceptionHandlingCallSite):
* ftl/FTLJITCode.h:
* ftl/FTLJITFinalizer.cpp:
(JSC::FTL::JITFinalizer::finalizeFunction):
* ftl/FTLJITFinalizer.h:
* ftl/FTLJSCall.cpp:
(JSC::FTL::JSCall::emit):
* ftl/FTLJSCallBase.cpp:
(JSC::FTL::JSCallBase::emit):
* ftl/FTLJSTailCall.cpp:
(JSC::FTL::JSTailCall::JSTailCall):
(JSC::FTL::JSTailCall::emit):
(JSC::FTL::DFG::getRegisterWithAddend): Deleted.
(JSC::FTL::m_instructionOffset): Deleted.
* ftl/FTLJSTailCall.h:
(JSC::FTL::JSTailCall::patchpoint):
(JSC::FTL::JSTailCall::stackmapID):
(JSC::FTL::JSTailCall::estimatedSize):
(JSC::FTL::JSTailCall::operator<):
* ftl/FTLLowerDFGToLLVM.cpp:
(JSC::FTL::DFG::LowerDFGToLLVM::compileInvalidationPoint):
(JSC::FTL::DFG::LowerDFGToLLVM::appendOSRExitArgumentsForPatchpointIfWillCatchException):
(JSC::FTL::DFG::LowerDFGToLLVM::lowBlock):
(JSC::FTL::DFG::LowerDFGToLLVM::appendOSRExitDescriptor):
(JSC::FTL::DFG::LowerDFGToLLVM::appendOSRExit):
(JSC::FTL::DFG::LowerDFGToLLVM::blessSpeculation):
(JSC::FTL::DFG::LowerDFGToLLVM::emitOSRExitCall):
(JSC::FTL::DFG::LowerDFGToLLVM::buildExitArguments):
(JSC::FTL::DFG::LowerDFGToLLVM::callStackmap):
(JSC::FTL::lowerDFGToLLVM):
* ftl/FTLOSRExit.cpp:
(JSC::FTL::OSRExitDescriptor::OSRExitDescriptor):
(JSC::FTL::OSRExitDescriptor::validateReferences):
(JSC::FTL::OSRExitDescriptor::appendOSRExit):
(JSC::FTL::OSRExitDescriptor::appendOSRExitLater):
(JSC::FTL::OSRExitDescriptor::prepareOSRExitHandle):
(JSC::FTL::OSRExit::OSRExit):
(JSC::FTL::OSRExit::codeLocationForRepatch):
(JSC::FTL::OSRExit::gatherRegistersToSpillForCallIfException):
(JSC::FTL::OSRExit::spillRegistersToSpillSlot):
(JSC::FTL::OSRExit::recoverRegistersFromSpillSlot):
(JSC::FTL::OSRExit::willArriveAtExitFromIndirectExceptionCheck):
* ftl/FTLOSRExit.h:
(JSC::FTL::OSRExit::considerAddingAsFrequentExitSite):
* ftl/FTLOSRExitCompilationInfo.h:
(JSC::FTL::OSRExitCompilationInfo::OSRExitCompilationInfo):
* ftl/FTLOSRExitCompiler.cpp:
(JSC::FTL::reboxAccordingToFormat):
(JSC::FTL::compileRecovery):
(JSC::FTL::compileStub):
(JSC::FTL::compileFTLOSRExit):
* ftl/FTLOSRExitHandle.cpp: Added.
(JSC::FTL::OSRExitHandle::emitExitThunk):
* ftl/FTLOSRExitHandle.h: Added.
(JSC::FTL::OSRExitHandle::OSRExitHandle):
* ftl/FTLState.cpp:
(JSC::FTL::State::State):
(JSC::FTL::State::~State):

Source/WTF:

Make sure that this has perfect forwarding.

* wtf/SegmentedVector.h:
(WTF::SegmentedVector::append):
(WTF::SegmentedVector::alloc):



git-svn-id: http://svn.webkit.org/repository/webkit/trunk@193362 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/JavaScriptCore/CMakeLists.txt b/Source/JavaScriptCore/CMakeLists.txt
index 7eeecfe..a1866b5 100644
--- a/Source/JavaScriptCore/CMakeLists.txt
+++ b/Source/JavaScriptCore/CMakeLists.txt
@@ -1031,6 +1031,7 @@
         ftl/FTLOSREntry.cpp
         ftl/FTLOSRExit.cpp
         ftl/FTLOSRExitCompiler.cpp
+        ftl/FTLOSRExitHandle.cpp
         ftl/FTLOperations.cpp
         ftl/FTLOutput.cpp
         ftl/FTLRecoveryOpcode.cpp
diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog
index 67e2237..1510867 100644
--- a/Source/JavaScriptCore/ChangeLog
+++ b/Source/JavaScriptCore/ChangeLog
@@ -1,3 +1,125 @@
+2015-12-02  Filip Pizlo  <fpizlo@apple.com>
+
+        FTL B3 should support OSR exit
+        https://bugs.webkit.org/show_bug.cgi?id=151710
+
+        Reviewed by Saam Barati.
+
+        This adds OSR exit support using the same style that I established with lazy slow paths. All of
+        the work is driven by FTL::LowerDFGToLLVM, and from there any work that needs to be deferred
+        until after B3 finishes is attached to the stackmap generator. In order to make it easy to port
+        all of the different forms of OSR exit - invalidation points, exceptions, etc. - the logic for
+        registering an OSR exit is abstracted behind OSRExitDescriptor and OSRExitHandle.
+
+        An issue that I encountered repeatedly in this patch is OSRExitDescriptor being passed as a
+        reference (&) rather than pointer (*). The new code uses a lot of lambdas that run after the
+        current frame pops, so the capture list cannot be [&]. I believe that always listing all of the
+        captured variables is not scalable considering how sophisticated our use of lambdas is. So, it
+        makes sense to use [=]. But anytime we captured a variable whose type was OSRExitDescriptor&, it
+        would be captured by value, because that's how references work. One has to be mindful of these
+        things whenever using [=]. Note that it's not enough to say that we should have listed the
+        captured variables explicitly - in that case, we still could have made the mistake by forgetting
+        to put & in front of the variant. The pattern that worked for me to reason about whether I'm
+        capturing an object or a pointer to an object is to always use pointer types for pointers: either
+        RefPtr<> when we also want the lambda to prolong the object's life, or * if we are confident that
+        the object will stay alive. For this reason, this patch changes all code that references
+        OSRExitDescriptor to use * instead of &. Consistency makes the code easier to grok, and it made
+        it easier to introduce the required uses of * in places where there were lambdas.
+
+        I tested this by running imaging-gaussian-blur, and running some tests that reqiure OSR exit. I'm
+        not promising that all kinds of exits work, but we have to begin somewhere.
+
+        * CMakeLists.txt:
+        * JavaScriptCore.xcodeproj/project.pbxproj:
+        * b3/B3Compilation.cpp:
+        (JSC::B3::Compilation::Compilation):
+        (JSC::B3::Compilation::~Compilation):
+        * b3/B3Procedure.cpp:
+        (JSC::B3::Procedure::addDataSection):
+        (JSC::B3::Procedure::frameSize):
+        (JSC::B3::Procedure::calleeSaveRegisters):
+        * b3/B3Procedure.h:
+        (JSC::B3::Procedure::releaseByproducts):
+        (JSC::B3::Procedure::code):
+        (JSC::B3::Procedure::takeByproducts): Deleted.
+        * b3/air/AirCode.h:
+        (JSC::B3::Air::Code::setFrameSize):
+        (JSC::B3::Air::Code::calleeSaveRegisters):
+        * b3/air/AirGenerationContext.h:
+        * ftl/FTLB3Compile.cpp:
+        (JSC::FTL::compile):
+        * ftl/FTLCompile.cpp:
+        (JSC::FTL::mmAllocateDataSection):
+        * ftl/FTLExceptionHandlerManager.cpp:
+        (JSC::FTL::ExceptionHandlerManager::lazySlowPathExceptionTarget):
+        (JSC::FTL::ExceptionHandlerManager::getCallOSRExitCommon):
+        * ftl/FTLExitThunkGenerator.cpp:
+        * ftl/FTLExitThunkGenerator.h:
+        * ftl/FTLJITCode.cpp:
+        (JSC::FTL::JITCode::JITCode):
+        (JSC::FTL::JITCode::initializeB3Code):
+        (JSC::FTL::JITCode::initializeB3Byproducts):
+        (JSC::FTL::JITCode::initializeExitThunks):
+        (JSC::FTL::JITCode::validateReferences):
+        (JSC::FTL::JITCode::liveRegistersToPreserveAtExceptionHandlingCallSite):
+        * ftl/FTLJITCode.h:
+        * ftl/FTLJITFinalizer.cpp:
+        (JSC::FTL::JITFinalizer::finalizeFunction):
+        * ftl/FTLJITFinalizer.h:
+        * ftl/FTLJSCall.cpp:
+        (JSC::FTL::JSCall::emit):
+        * ftl/FTLJSCallBase.cpp:
+        (JSC::FTL::JSCallBase::emit):
+        * ftl/FTLJSTailCall.cpp:
+        (JSC::FTL::JSTailCall::JSTailCall):
+        (JSC::FTL::JSTailCall::emit):
+        (JSC::FTL::DFG::getRegisterWithAddend): Deleted.
+        (JSC::FTL::m_instructionOffset): Deleted.
+        * ftl/FTLJSTailCall.h:
+        (JSC::FTL::JSTailCall::patchpoint):
+        (JSC::FTL::JSTailCall::stackmapID):
+        (JSC::FTL::JSTailCall::estimatedSize):
+        (JSC::FTL::JSTailCall::operator<):
+        * ftl/FTLLowerDFGToLLVM.cpp:
+        (JSC::FTL::DFG::LowerDFGToLLVM::compileInvalidationPoint):
+        (JSC::FTL::DFG::LowerDFGToLLVM::appendOSRExitArgumentsForPatchpointIfWillCatchException):
+        (JSC::FTL::DFG::LowerDFGToLLVM::lowBlock):
+        (JSC::FTL::DFG::LowerDFGToLLVM::appendOSRExitDescriptor):
+        (JSC::FTL::DFG::LowerDFGToLLVM::appendOSRExit):
+        (JSC::FTL::DFG::LowerDFGToLLVM::blessSpeculation):
+        (JSC::FTL::DFG::LowerDFGToLLVM::emitOSRExitCall):
+        (JSC::FTL::DFG::LowerDFGToLLVM::buildExitArguments):
+        (JSC::FTL::DFG::LowerDFGToLLVM::callStackmap):
+        (JSC::FTL::lowerDFGToLLVM):
+        * ftl/FTLOSRExit.cpp:
+        (JSC::FTL::OSRExitDescriptor::OSRExitDescriptor):
+        (JSC::FTL::OSRExitDescriptor::validateReferences):
+        (JSC::FTL::OSRExitDescriptor::appendOSRExit):
+        (JSC::FTL::OSRExitDescriptor::appendOSRExitLater):
+        (JSC::FTL::OSRExitDescriptor::prepareOSRExitHandle):
+        (JSC::FTL::OSRExit::OSRExit):
+        (JSC::FTL::OSRExit::codeLocationForRepatch):
+        (JSC::FTL::OSRExit::gatherRegistersToSpillForCallIfException):
+        (JSC::FTL::OSRExit::spillRegistersToSpillSlot):
+        (JSC::FTL::OSRExit::recoverRegistersFromSpillSlot):
+        (JSC::FTL::OSRExit::willArriveAtExitFromIndirectExceptionCheck):
+        * ftl/FTLOSRExit.h:
+        (JSC::FTL::OSRExit::considerAddingAsFrequentExitSite):
+        * ftl/FTLOSRExitCompilationInfo.h:
+        (JSC::FTL::OSRExitCompilationInfo::OSRExitCompilationInfo):
+        * ftl/FTLOSRExitCompiler.cpp:
+        (JSC::FTL::reboxAccordingToFormat):
+        (JSC::FTL::compileRecovery):
+        (JSC::FTL::compileStub):
+        (JSC::FTL::compileFTLOSRExit):
+        * ftl/FTLOSRExitHandle.cpp: Added.
+        (JSC::FTL::OSRExitHandle::emitExitThunk):
+        * ftl/FTLOSRExitHandle.h: Added.
+        (JSC::FTL::OSRExitHandle::OSRExitHandle):
+        * ftl/FTLState.cpp:
+        (JSC::FTL::State::State):
+        (JSC::FTL::State::~State):
+
 2015-12-03  Joseph Pecoraro  <pecoraro@apple.com>
 
         REGRESSION:(r192753): Remote Web Inspector: RemoteInspector::sendMessageToRemote with null connection
diff --git a/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj b/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
index d23b080..912dbb0 100644
--- a/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
+++ b/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
@@ -494,6 +494,9 @@
 		0F978B3B1AAEA71D007C7369 /* ConstantMode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F978B3A1AAEA71D007C7369 /* ConstantMode.cpp */; };
 		0F98206016BFE38100240D02 /* PreciseJumpTargets.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F98205D16BFE37F00240D02 /* PreciseJumpTargets.cpp */; };
 		0F98206116BFE38300240D02 /* PreciseJumpTargets.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F98205E16BFE37F00240D02 /* PreciseJumpTargets.h */; settings = {ATTRIBUTES = (Private, ); }; };
+		0F9B1DB41C0E42A500E5BFD2 /* FTLB3Output.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F9B1DB31C0E42A500E5BFD2 /* FTLB3Output.cpp */; };
+		0F9B1DB71C0E42BD00E5BFD2 /* FTLOSRExitHandle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F9B1DB51C0E42BD00E5BFD2 /* FTLOSRExitHandle.cpp */; };
+		0F9B1DB81C0E42BD00E5BFD2 /* FTLOSRExitHandle.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F9B1DB61C0E42BD00E5BFD2 /* FTLOSRExitHandle.h */; };
 		0F9C5E5E18E35F5E00D431C3 /* FTLDWARFRegister.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F9C5E5C18E35F5E00D431C3 /* FTLDWARFRegister.cpp */; };
 		0F9C5E5F18E35F5E00D431C3 /* FTLDWARFRegister.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F9C5E5D18E35F5E00D431C3 /* FTLDWARFRegister.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		0F9D3370165DBB90005AD387 /* Disassembler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F9D336E165DBB8D005AD387 /* Disassembler.cpp */; };
@@ -1092,7 +1095,6 @@
 		26718BA51BE99F780052017B /* AirIteratedRegisterCoalescing.h in Headers */ = {isa = PBXBuildFile; fileRef = 26718BA31BE99F780052017B /* AirIteratedRegisterCoalescing.h */; };
 		2684D4381C00161C0081D663 /* AirLiveness.h in Headers */ = {isa = PBXBuildFile; fileRef = 2684D4371C00161C0081D663 /* AirLiveness.h */; };
 		269D636E1BFBE5D100101B1D /* FTLB3Output.h in Headers */ = {isa = PBXBuildFile; fileRef = 269D636D1BFBE5D000101B1D /* FTLB3Output.h */; };
-		26BB57601BFC4328005F12EB /* FTLB3Output.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BB575F1BFC4328005F12EB /* FTLB3Output.cpp */; };
 		2A05ABD51961DF2400341750 /* JSPropertyNameEnumerator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2A05ABD31961DF2400341750 /* JSPropertyNameEnumerator.cpp */; };
 		2A05ABD61961DF2400341750 /* JSPropertyNameEnumerator.h in Headers */ = {isa = PBXBuildFile; fileRef = 2A05ABD41961DF2400341750 /* JSPropertyNameEnumerator.h */; };
 		2A111245192FCE79005EE18D /* CustomGetterSetter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2A111243192FCE79005EE18D /* CustomGetterSetter.cpp */; };
@@ -2580,6 +2582,9 @@
 		0F978B3A1AAEA71D007C7369 /* ConstantMode.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ConstantMode.cpp; sourceTree = "<group>"; };
 		0F98205D16BFE37F00240D02 /* PreciseJumpTargets.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PreciseJumpTargets.cpp; sourceTree = "<group>"; };
 		0F98205E16BFE37F00240D02 /* PreciseJumpTargets.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PreciseJumpTargets.h; sourceTree = "<group>"; };
+		0F9B1DB31C0E42A500E5BFD2 /* FTLB3Output.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FTLB3Output.cpp; path = ftl/FTLB3Output.cpp; sourceTree = "<group>"; };
+		0F9B1DB51C0E42BD00E5BFD2 /* FTLOSRExitHandle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FTLOSRExitHandle.cpp; path = ftl/FTLOSRExitHandle.cpp; sourceTree = "<group>"; };
+		0F9B1DB61C0E42BD00E5BFD2 /* FTLOSRExitHandle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FTLOSRExitHandle.h; path = ftl/FTLOSRExitHandle.h; sourceTree = "<group>"; };
 		0F9C5E5C18E35F5E00D431C3 /* FTLDWARFRegister.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FTLDWARFRegister.cpp; path = ftl/FTLDWARFRegister.cpp; sourceTree = "<group>"; };
 		0F9C5E5D18E35F5E00D431C3 /* FTLDWARFRegister.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FTLDWARFRegister.h; path = ftl/FTLDWARFRegister.h; sourceTree = "<group>"; };
 		0F9D336E165DBB8D005AD387 /* Disassembler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Disassembler.cpp; path = disassembler/Disassembler.cpp; sourceTree = "<group>"; };
@@ -3147,7 +3152,6 @@
 		26718BA31BE99F780052017B /* AirIteratedRegisterCoalescing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AirIteratedRegisterCoalescing.h; path = b3/air/AirIteratedRegisterCoalescing.h; sourceTree = "<group>"; };
 		2684D4371C00161C0081D663 /* AirLiveness.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AirLiveness.h; path = b3/air/AirLiveness.h; sourceTree = "<group>"; };
 		269D636D1BFBE5D000101B1D /* FTLB3Output.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FTLB3Output.h; path = ftl/FTLB3Output.h; sourceTree = "<group>"; };
-		26BB575F1BFC4328005F12EB /* FTLB3Output.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FTLB3Output.cpp; path = ftl/FTLB3Output.cpp; sourceTree = "<group>"; };
 		2A05ABD31961DF2400341750 /* JSPropertyNameEnumerator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSPropertyNameEnumerator.cpp; sourceTree = "<group>"; };
 		2A05ABD41961DF2400341750 /* JSPropertyNameEnumerator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSPropertyNameEnumerator.h; sourceTree = "<group>"; };
 		2A111243192FCE79005EE18D /* CustomGetterSetter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CustomGetterSetter.cpp; sourceTree = "<group>"; };
@@ -4424,6 +4428,7 @@
 				0F485323187DFDEC0083B687 /* FTLAvailableRecovery.cpp */,
 				0F485324187DFDEC0083B687 /* FTLAvailableRecovery.h */,
 				0FB387911BFD31A100E3AB1E /* FTLB3Compile.cpp */,
+				0F9B1DB31C0E42A500E5BFD2 /* FTLB3Output.cpp */,
 				269D636D1BFBE5D000101B1D /* FTLB3Output.h */,
 				0FEA09FE170513DB00BB722C /* FTLCapabilities.cpp */,
 				0FEA09FF170513DB00BB722C /* FTLCapabilities.h */,
@@ -4496,6 +4501,8 @@
 				0F235BC817178E1C00690C7F /* FTLOSRExitCompilationInfo.h */,
 				0F235BC917178E1C00690C7F /* FTLOSRExitCompiler.cpp */,
 				0F235BCA17178E1C00690C7F /* FTLOSRExitCompiler.h */,
+				0F9B1DB51C0E42BD00E5BFD2 /* FTLOSRExitHandle.cpp */,
+				0F9B1DB61C0E42BD00E5BFD2 /* FTLOSRExitHandle.h */,
 				0FEA0A291709629600BB722C /* FTLOutput.cpp */,
 				0FEA0A06170513DB00BB722C /* FTLOutput.h */,
 				0F485325187DFDEC0083B687 /* FTLRecoveryOpcode.cpp */,
@@ -7575,6 +7582,7 @@
 				A700873E17CBE8D300C3E643 /* MapPrototype.h in Headers */,
 				C2B916C214DA014E00CBAC86 /* MarkedAllocator.h in Headers */,
 				142D6F0913539A2800B02E86 /* MarkedBlock.h in Headers */,
+				0F9B1DB81C0E42BD00E5BFD2 /* FTLOSRExitHandle.h in Headers */,
 				141448CB13A176EC00F5BA1A /* MarkedBlockSet.h in Headers */,
 				14D2F3DB139F4BE200491031 /* MarkedSpace.h in Headers */,
 				142D6F1213539A4100B02E86 /* MarkStack.h in Headers */,
@@ -8618,6 +8626,7 @@
 				0FD8A32717D51F5700CA2C40 /* DFGTierUpCheckInjectionPhase.cpp in Sources */,
 				0FD8A32917D51F5700CA2C40 /* DFGToFTLDeferredCompilationCallback.cpp in Sources */,
 				0FD8A32B17D51F5700CA2C40 /* DFGToFTLForOSREntryDeferredCompilationCallback.cpp in Sources */,
+				0F9B1DB41C0E42A500E5BFD2 /* FTLB3Output.cpp in Sources */,
 				0FE7211D193B9C590031F6ED /* DFGTransition.cpp in Sources */,
 				0F63944015C75F1D006A597C /* DFGTypeCheckHoistingPhase.cpp in Sources */,
 				0FBE0F7616C1DB0F0082C5E8 /* DFGUnificationPhase.cpp in Sources */,
@@ -8832,6 +8841,7 @@
 				A51007C0187CC3C600B38879 /* JSGlobalObjectInspectorController.cpp in Sources */,
 				A50E4B6318809DD50068A46D /* JSGlobalObjectRuntimeAgent.cpp in Sources */,
 				A503FA29188F105900110F14 /* JSGlobalObjectScriptDebugServer.cpp in Sources */,
+				0F9B1DB71C0E42BD00E5BFD2 /* FTLOSRExitHandle.cpp in Sources */,
 				A513E5BF185BFACC007E95AD /* JSInjectedScriptHost.cpp in Sources */,
 				A513E5C1185BFACC007E95AD /* JSInjectedScriptHostPrototype.cpp in Sources */,
 				E33F50801B8429A400413856 /* JSInternalPromise.cpp in Sources */,
@@ -8987,7 +8997,6 @@
 				0F15CD221BA5F9860031FFD3 /* PutByIdFlags.cpp in Sources */,
 				0F9332A314CA7DD70085F3C6 /* PutByIdStatus.cpp in Sources */,
 				0F93B4A918B92C4D00178A3F /* PutByIdVariant.cpp in Sources */,
-				26BB57601BFC4328005F12EB /* FTLB3Output.cpp in Sources */,
 				0FF60AC316740F8800029779 /* ReduceWhitespace.cpp in Sources */,
 				E33637A51B63220200EE0840 /* ReflectObject.cpp in Sources */,
 				0FA7A8EB18B413C80052371D /* Reg.cpp in Sources */,
diff --git a/Source/JavaScriptCore/b3/B3Compilation.cpp b/Source/JavaScriptCore/b3/B3Compilation.cpp
index 0e28c8f..f6fc30e 100644
--- a/Source/JavaScriptCore/b3/B3Compilation.cpp
+++ b/Source/JavaScriptCore/b3/B3Compilation.cpp
@@ -42,13 +42,14 @@
 {
     TimingScope timingScope("Compilation");
     
-    CCallHelpers jit(&vm);
     prepareForGeneration(proc, optLevel);
+    
+    CCallHelpers jit(&vm);
     generate(proc, jit);
     LinkBuffer linkBuffer(vm, jit, nullptr);
 
     m_codeRef = FINALIZE_CODE(linkBuffer, ("B3::Compilation"));
-    m_byproducts = proc.takeByproducts();
+    m_byproducts = proc.releaseByproducts();
 }
 
 Compilation::~Compilation()
diff --git a/Source/JavaScriptCore/b3/B3Procedure.cpp b/Source/JavaScriptCore/b3/B3Procedure.cpp
index 066a3c2..9b4ecf8 100644
--- a/Source/JavaScriptCore/b3/B3Procedure.cpp
+++ b/Source/JavaScriptCore/b3/B3Procedure.cpp
@@ -148,7 +148,12 @@
     return result;
 }
 
-const RegisterAtOffsetList& Procedure::calleeSaveRegisters()
+unsigned Procedure::frameSize() const
+{
+    return code().frameSize();
+}
+
+const RegisterAtOffsetList& Procedure::calleeSaveRegisters() const
 {
     return code().calleeSaveRegisters();
 }
diff --git a/Source/JavaScriptCore/b3/B3Procedure.h b/Source/JavaScriptCore/b3/B3Procedure.h
index 82903d9..9e6c73f 100644
--- a/Source/JavaScriptCore/b3/B3Procedure.h
+++ b/Source/JavaScriptCore/b3/B3Procedure.h
@@ -219,11 +219,13 @@
     // just keeps alive things like the double constant pool and switch lookup tables. If this sounds
     // confusing, you should probably be using the B3::Compilation API to compile code. If you use
     // that API, then you don't have to worry about this.
-    std::unique_ptr<OpaqueByproducts> takeByproducts() { return WTF::move(m_byproducts); }
+    std::unique_ptr<OpaqueByproducts> releaseByproducts() { return WTF::move(m_byproducts); }
 
+    const Air::Code& code() const { return *m_code; }
     Air::Code& code() { return *m_code; }
 
-    const RegisterAtOffsetList& calleeSaveRegisters();
+    unsigned frameSize() const;
+    const RegisterAtOffsetList& calleeSaveRegisters() const;
 
 private:
     friend class BlockInsertionSet;
diff --git a/Source/JavaScriptCore/b3/air/AirCode.h b/Source/JavaScriptCore/b3/air/AirCode.h
index 8db90a6..f626125 100644
--- a/Source/JavaScriptCore/b3/air/AirCode.h
+++ b/Source/JavaScriptCore/b3/air/AirCode.h
@@ -103,6 +103,7 @@
         m_frameSize = frameSize;
     }
 
+    const RegisterAtOffsetList& calleeSaveRegisters() const { return m_calleeSaveRegisters; }
     RegisterAtOffsetList& calleeSaveRegisters() { return m_calleeSaveRegisters; }
 
     // Recomputes predecessors and deletes unreachable blocks.
diff --git a/Source/JavaScriptCore/b3/air/AirGenerationContext.h b/Source/JavaScriptCore/b3/air/AirGenerationContext.h
index 9cf8b4f..b6a27e9 100644
--- a/Source/JavaScriptCore/b3/air/AirGenerationContext.h
+++ b/Source/JavaScriptCore/b3/air/AirGenerationContext.h
@@ -29,9 +29,12 @@
 #if ENABLE(B3_JIT)
 
 #include <wtf/SharedTask.h>
+#include <wtf/Vector.h>
 
 namespace JSC { namespace B3 { namespace Air {
 
+class Code;
+
 struct GenerationContext {
     typedef void LatePathFunction(CCallHelpers&, GenerationContext&);
     typedef SharedTask<LatePathFunction> LatePath;
diff --git a/Source/JavaScriptCore/ftl/FTLB3Compile.cpp b/Source/JavaScriptCore/ftl/FTLB3Compile.cpp
index 133b42f..fcc7074 100644
--- a/Source/JavaScriptCore/ftl/FTLB3Compile.cpp
+++ b/Source/JavaScriptCore/ftl/FTLB3Compile.cpp
@@ -77,6 +77,8 @@
         dataLog("    ", *registerOffsets, "\n");
     }
     state.graph.m_codeBlock->setCalleeSaveRegisters(WTF::move(registerOffsets));
+    ASSERT(!(state.proc->frameSize() % sizeof(EncodedJSValue)));
+    state.jitCode->common.frameRegisterCount = state.proc->frameSize() / sizeof(EncodedJSValue);
 
     CCallHelpers jit(&vm, codeBlock);
     B3::generate(*state.proc, jit);
@@ -90,6 +92,7 @@
 
     state.generatedFunction = bitwise_cast<GeneratedFunction>(
         state.finalizer->b3CodeLinkBuffer->entrypoint().executableAddress());
+    state.jitCode->initializeB3Byproducts(state.proc->releaseByproducts());
 }
 
 } } // namespace JSC::FTL
diff --git a/Source/JavaScriptCore/ftl/FTLCompile.cpp b/Source/JavaScriptCore/ftl/FTLCompile.cpp
index 2331163..6c44229 100644
--- a/Source/JavaScriptCore/ftl/FTLCompile.cpp
+++ b/Source/JavaScriptCore/ftl/FTLCompile.cpp
@@ -498,7 +498,7 @@
         for (unsigned j = 0; j < iter->value.size(); j++) {
             {
                 uint32_t stackmapRecordIndex = iter->value[j].index;
-                OSRExit exit(exitDescriptor, stackmapRecordIndex);
+                OSRExit exit(&exitDescriptor, stackmapRecordIndex);
                 state.jitCode->osrExit.append(exit);
                 state.finalizer->osrExit.append(OSRExitCompilationInfo());
             }
@@ -506,8 +506,8 @@
             OSRExit& exit = state.jitCode->osrExit.last();
             if (exit.willArriveAtExitFromIndirectExceptionCheck()) {
                 StackMaps::Record& record = iter->value[j].record;
-                RELEASE_ASSERT(exit.m_descriptor.m_semanticCodeOriginForCallFrameHeader.isSet());
-                CallSiteIndex callSiteIndex = state.jitCode->common.addUniqueCallSiteIndex(exit.m_descriptor.m_semanticCodeOriginForCallFrameHeader);
+                RELEASE_ASSERT(exit.m_descriptor->m_semanticCodeOriginForCallFrameHeader.isSet());
+                CallSiteIndex callSiteIndex = state.jitCode->common.addUniqueCallSiteIndex(exit.m_descriptor->m_semanticCodeOriginForCallFrameHeader);
                 exit.m_exceptionHandlerCallSiteIndex = callSiteIndex;
 
                 OSRExit* callOperationExit = nullptr;
@@ -523,12 +523,12 @@
                     // and the other that will be arrived at from the callOperation exception handler path.
                     // This code here generates the second callOperation variant.
                     uint32_t stackmapRecordIndex = iter->value[j].index;
-                    OSRExit exit(exitDescriptor, stackmapRecordIndex);
+                    OSRExit exit(&exitDescriptor, stackmapRecordIndex);
                     if (exitDescriptor.m_exceptionType == ExceptionType::GetById)
                         exit.m_exceptionType = ExceptionType::GetByIdCallOperation;
                     else
                         exit.m_exceptionType = ExceptionType::PutByIdCallOperation;
-                    CallSiteIndex callSiteIndex = state.jitCode->common.addUniqueCallSiteIndex(exit.m_descriptor.m_semanticCodeOriginForCallFrameHeader);
+                    CallSiteIndex callSiteIndex = state.jitCode->common.addUniqueCallSiteIndex(exit.m_descriptor->m_semanticCodeOriginForCallFrameHeader);
                     exit.m_exceptionHandlerCallSiteIndex = callSiteIndex;
 
                     state.jitCode->osrExit.append(exit);
@@ -586,13 +586,13 @@
             OSRExit& exit = state.jitCode->osrExit[i];
             
             if (verboseCompilationEnabled())
-                dataLog("Handling OSR stackmap #", exit.m_descriptor.m_stackmapID, " for ", exit.m_codeOrigin, "\n");
+                dataLog("Handling OSR stackmap #", exit.m_descriptor->m_stackmapID, " for ", exit.m_codeOrigin, "\n");
 
             info.m_thunkAddress = linkBuffer->locationOf(info.m_thunkLabel);
             exit.m_patchableCodeOffset = linkBuffer->offsetOf(info.m_thunkJump);
 
             if (exit.willArriveAtOSRExitFromGenericUnwind()) {
-                HandlerInfo newHandler = exit.m_descriptor.m_baselineExceptionHandler;
+                HandlerInfo newHandler = exit.m_descriptor->m_baselineExceptionHandler;
                 newHandler.start = exit.m_exceptionHandlerCallSiteIndex.bits();
                 newHandler.end = exit.m_exceptionHandlerCallSiteIndex.bits() + 1;
                 newHandler.nativeCode = info.m_thunkAddress;
@@ -601,10 +601,10 @@
 
             if (verboseCompilationEnabled()) {
                 DumpContext context;
-                dataLog("    Exit values: ", inContext(exit.m_descriptor.m_values, &context), "\n");
-                if (!exit.m_descriptor.m_materializations.isEmpty()) {
+                dataLog("    Exit values: ", inContext(exit.m_descriptor->m_values, &context), "\n");
+                if (!exit.m_descriptor->m_materializations.isEmpty()) {
                     dataLog("    Materializations: \n");
-                    for (ExitTimeObjectMaterialization* materialization : exit.m_descriptor.m_materializations)
+                    for (ExitTimeObjectMaterialization* materialization : exit.m_descriptor->m_materializations)
                         dataLog("        Materialize(", pointerDump(materialization), ")\n");
                 }
             }
@@ -997,7 +997,7 @@
         
         codeAddresses.append(bitwise_cast<char*>(generatedFunction) + record.instructionOffset + MacroAssembler::maxJumpReplacementSize());
         
-        if (exit.m_descriptor.m_isInvalidationPoint)
+        if (exit.m_descriptor->m_isInvalidationPoint)
             jitCode->common.jumpReplacements.append(JumpReplacement(source, info.m_thunkAddress));
         else
             MacroAssembler::replaceWithJump(source, info.m_thunkAddress);
diff --git a/Source/JavaScriptCore/ftl/FTLExceptionHandlerManager.cpp b/Source/JavaScriptCore/ftl/FTLExceptionHandlerManager.cpp
index 5b1713a..d79111a 100644
--- a/Source/JavaScriptCore/ftl/FTLExceptionHandlerManager.cpp
+++ b/Source/JavaScriptCore/ftl/FTLExceptionHandlerManager.cpp
@@ -82,7 +82,7 @@
         return CodeLocationLabel();
 
     size_t osrExitIndex = findResult->value;
-    RELEASE_ASSERT(m_state.jitCode->osrExit[osrExitIndex].m_descriptor.m_exceptionType == ExceptionType::LazySlowPath);
+    RELEASE_ASSERT(m_state.jitCode->osrExit[osrExitIndex].m_descriptor->m_exceptionType == ExceptionType::LazySlowPath);
     OSRExitCompilationInfo& info = m_state.finalizer->osrExit[osrExitIndex];
     RELEASE_ASSERT(info.m_thunkLabel.isSet());
     return m_state.finalizer->exitThunksLinkBuffer->locationOf(info.m_thunkLabel);
@@ -109,7 +109,7 @@
         return nullptr;
     size_t osrExitIndex = findResult->value;
     OSRExit* exit = &m_state.jitCode->osrExit[osrExitIndex];
-    RELEASE_ASSERT(exit->m_descriptor.m_exceptionType == ExceptionType::JSCall);
+    RELEASE_ASSERT(exit->m_descriptor->m_exceptionType == ExceptionType::JSCall);
     return exit; 
 }
 
diff --git a/Source/JavaScriptCore/ftl/FTLExitThunkGenerator.cpp b/Source/JavaScriptCore/ftl/FTLExitThunkGenerator.cpp
index 7abe54a..f04f703 100644
--- a/Source/JavaScriptCore/ftl/FTLExitThunkGenerator.cpp
+++ b/Source/JavaScriptCore/ftl/FTLExitThunkGenerator.cpp
@@ -26,7 +26,7 @@
 #include "config.h"
 #include "FTLExitThunkGenerator.h"
 
-#if ENABLE(FTL_JIT)
+#if ENABLE(FTL_JIT) && !FTL_USES_B3
 
 #include "FTLOSRExitCompilationInfo.h"
 #include "FTLState.h"
@@ -79,5 +79,5 @@
 
 } } // namespace JSC::FTL
 
-#endif // ENABLE(FTL_JIT)
+#endif // ENABLE(FTL_JIT) && !FTL_USES_B3
 
diff --git a/Source/JavaScriptCore/ftl/FTLExitThunkGenerator.h b/Source/JavaScriptCore/ftl/FTLExitThunkGenerator.h
index f553f01..d49d344 100644
--- a/Source/JavaScriptCore/ftl/FTLExitThunkGenerator.h
+++ b/Source/JavaScriptCore/ftl/FTLExitThunkGenerator.h
@@ -26,7 +26,9 @@
 #ifndef FTLExitThunkGenerator_h
 #define FTLExitThunkGenerator_h
 
-#if ENABLE(FTL_JIT)
+#include "DFGCommon.h"
+
+#if ENABLE(FTL_JIT) && !FTL_USES_B3
 
 #include "CCallHelpers.h"
 
@@ -52,7 +54,7 @@
 
 } } // namespace JSC::FTL
 
-#endif // ENABLE(FTL_JIT)
+#endif // ENABLE(FTL_JIT) && !FTL_USES_B3
 
 #endif // FTLExitThunkGenerator_h
 
diff --git a/Source/JavaScriptCore/ftl/FTLJITCode.cpp b/Source/JavaScriptCore/ftl/FTLJITCode.cpp
index 7851ca5..48dcd34 100644
--- a/Source/JavaScriptCore/ftl/FTLJITCode.cpp
+++ b/Source/JavaScriptCore/ftl/FTLJITCode.cpp
@@ -32,6 +32,8 @@
 
 namespace JSC { namespace FTL {
 
+using namespace B3;
+
 JITCode::JITCode()
     : JSC::JITCode(FTLJIT)
 {
@@ -60,6 +62,11 @@
 {
     m_b3Code = b3Code;
 }
+
+void JITCode::initializeB3Byproducts(std::unique_ptr<OpaqueByproducts> byproducts)
+{
+    m_b3Byproducts = WTF::move(byproducts);
+}
 #else // FTL_USES_B3
 void JITCode::initializeExitThunks(CodeRef exitThunks)
 {
@@ -155,17 +162,21 @@
     common.validateReferences(trackedReferences);
     
     for (OSRExit& exit : osrExit)
-        exit.m_descriptor.validateReferences(trackedReferences);
+        exit.m_descriptor->validateReferences(trackedReferences);
 }
 
 RegisterSet JITCode::liveRegistersToPreserveAtExceptionHandlingCallSite(CodeBlock*, CallSiteIndex callSiteIndex)
 {
+#if FTL_USES_B3
+    UNUSED_PARAM(callSiteIndex);
+#else // FTL_USES_B3
     for (OSRExit& exit : osrExit) {
         if (exit.m_exceptionHandlerCallSiteIndex.bits() == callSiteIndex.bits()) {
             RELEASE_ASSERT(exit.m_isExceptionHandler);
             return stackmaps.records[exit.m_stackmapRecordIndex].usedRegisterSet();
         }
     }
+#endif // FTL_USES_B3
     return RegisterSet();
 }
 
diff --git a/Source/JavaScriptCore/ftl/FTLJITCode.h b/Source/JavaScriptCore/ftl/FTLJITCode.h
index 465a687..69f76e6 100644
--- a/Source/JavaScriptCore/ftl/FTLJITCode.h
+++ b/Source/JavaScriptCore/ftl/FTLJITCode.h
@@ -28,6 +28,7 @@
 
 #if ENABLE(FTL_JIT)
 
+#include "B3OpaqueByproducts.h"
 #include "DFGCommonData.h"
 #include "FTLDataSection.h"
 #include "FTLLazySlowPath.h"
@@ -68,6 +69,7 @@
 
 #if FTL_USES_B3
     void initializeB3Code(CodeRef);
+    void initializeB3Byproducts(std::unique_ptr<B3::OpaqueByproducts>);
 #else
     void initializeExitThunks(CodeRef);
     void addHandle(PassRefPtr<ExecutableMemoryHandle>);
@@ -95,13 +97,16 @@
     DFG::CommonData common;
     SegmentedVector<OSRExit, 8> osrExit;
     SegmentedVector<OSRExitDescriptor, 8> osrExitDescriptors;
+#if !FTL_USES_B3
     StackMaps stackmaps;
+#endif // !FTL_USES_B3
     Vector<std::unique_ptr<LazySlowPath>> lazySlowPaths;
     
 private:
     CodePtr m_addressForCall;
 #if FTL_USES_B3
     CodeRef m_b3Code;
+    std::unique_ptr<B3::OpaqueByproducts> m_b3Byproducts;
 #else
     Vector<RefPtr<DataSection>> m_dataSections;
     Vector<RefPtr<ExecutableMemoryHandle>> m_handles;
diff --git a/Source/JavaScriptCore/ftl/FTLJITFinalizer.cpp b/Source/JavaScriptCore/ftl/FTLJITFinalizer.cpp
index 5781335..8daff4e 100644
--- a/Source/JavaScriptCore/ftl/FTLJITFinalizer.cpp
+++ b/Source/JavaScriptCore/ftl/FTLJITFinalizer.cpp
@@ -82,13 +82,6 @@
     bool dumpDisassembly = shouldDumpDisassembly() || Options::asyncDisassembly();
     
 #if FTL_USES_B3
-    for (OSRExitCompilationInfo& info : osrExit) {
-        b3CodeLinkBuffer->link(
-            info.m_thunkJump,
-            CodeLocationLabel(
-                m_plan.vm.getCTIStub(osrExitGenerationThunkGenerator).code()));
-    }
-    
     jitCode->initializeB3Code(
         FINALIZE_CODE_IF(
             dumpDisassembly, *b3CodeLinkBuffer,
diff --git a/Source/JavaScriptCore/ftl/FTLJITFinalizer.h b/Source/JavaScriptCore/ftl/FTLJITFinalizer.h
index 694a4d4..fd2a7a1 100644
--- a/Source/JavaScriptCore/ftl/FTLJITFinalizer.h
+++ b/Source/JavaScriptCore/ftl/FTLJITFinalizer.h
@@ -73,9 +73,9 @@
     std::unique_ptr<LinkBuffer> sideCodeLinkBuffer;
     std::unique_ptr<LinkBuffer> handleExceptionsLinkBuffer;
     Vector<OutOfLineCodeInfo> outOfLineCodeInfos;
-#endif
-    
     Vector<OSRExitCompilationInfo> osrExit;
+#endif
+
     Vector<CCallHelpers::Jump> lazySlowPathGeneratorJumps;
     GeneratedFunction function;
     RefPtr<JITCode> jitCode;
diff --git a/Source/JavaScriptCore/ftl/FTLJSCall.cpp b/Source/JavaScriptCore/ftl/FTLJSCall.cpp
index bfb8dd5..476b5e7 100644
--- a/Source/JavaScriptCore/ftl/FTLJSCall.cpp
+++ b/Source/JavaScriptCore/ftl/FTLJSCall.cpp
@@ -54,7 +54,11 @@
 {
     JSCallBase::emit(jit, state, osrExitFromGenericUnwindSpillSlots);
 
+#if FTL_USES_B3
+    jit.addPtr(CCallHelpers::TrustedImm32(- static_cast<int64_t>(state.jitCode->common.frameRegisterCount * sizeof(EncodedJSValue))), CCallHelpers::framePointerRegister, CCallHelpers::stackPointerRegister);
+#else // FTL_USES_B3
     jit.addPtr(CCallHelpers::TrustedImm32(- static_cast<int64_t>(state.jitCode->stackmaps.stackSizeForLocals())), CCallHelpers::framePointerRegister, CCallHelpers::stackPointerRegister);
+#endif // FTL_USES_B3
 }
 
 } } // namespace JSC::FTL
diff --git a/Source/JavaScriptCore/ftl/FTLJSCallBase.cpp b/Source/JavaScriptCore/ftl/FTLJSCallBase.cpp
index 972c5c0..11d6de2 100644
--- a/Source/JavaScriptCore/ftl/FTLJSCallBase.cpp
+++ b/Source/JavaScriptCore/ftl/FTLJSCallBase.cpp
@@ -54,9 +54,13 @@
 void JSCallBase::emit(CCallHelpers& jit, State& /*state*/, int32_t osrExitFromGenericUnwindStackSpillSlot)
 {
     RELEASE_ASSERT(!!m_callSiteIndex);
-    
+
+#if FTL_USES_B3
+    UNUSED_PARAM(osrExitFromGenericUnwindStackSpillSlot);
+#else // FTL_USES_B3
     if (m_correspondingGenericUnwindOSRExit)
         m_correspondingGenericUnwindOSRExit->spillRegistersToSpillSlot(jit, osrExitFromGenericUnwindStackSpillSlot);
+#endif // FTL_USES_B3
 
     jit.store32(CCallHelpers::TrustedImm32(m_callSiteIndex.bits()), CCallHelpers::tagFor(static_cast<VirtualRegister>(JSStack::ArgumentCount)));
 
diff --git a/Source/JavaScriptCore/ftl/FTLJSTailCall.cpp b/Source/JavaScriptCore/ftl/FTLJSTailCall.cpp
index c4b2f95..ba11225 100644
--- a/Source/JavaScriptCore/ftl/FTLJSTailCall.cpp
+++ b/Source/JavaScriptCore/ftl/FTLJSTailCall.cpp
@@ -38,8 +38,27 @@
 
 namespace JSC { namespace FTL {
 
+using namespace B3;
 using namespace DFG;
 
+#if FTL_USES_B3
+
+JSTailCall::JSTailCall(PatchpointValue* patchpoint, Node* node, const Vector<ExitValue>& arguments)
+    : JSCallBase(CallLinkInfo::TailCall, node->origin.semantic, node->origin.semantic)
+    , m_patchpoint(patchpoint)
+    , m_arguments(arguments)
+    , m_instructionOffset(0)
+{
+    UNREACHABLE_FOR_PLATFORM();
+}
+
+void JSTailCall::emit(JITCode&, CCallHelpers&)
+{
+    UNREACHABLE_FOR_PLATFORM();
+}
+
+#else // FTL_USES_B3
+
 namespace {
 
 FTL::Location getRegisterWithAddend(const ExitValue& value, StackMaps::Record& record, StackMaps& stackmaps)
@@ -180,10 +199,10 @@
 
 } // anonymous namespace
 
-JSTailCall::JSTailCall(unsigned stackmapID, Node* node, Vector<ExitValue> arguments)
+JSTailCall::JSTailCall(unsigned stackmapID, Node* node, const Vector<ExitValue>& arguments)
     : JSCallBase(CallLinkInfo::TailCall, node->origin.semantic, node->origin.semantic)
     , m_stackmapID(stackmapID)
-    , m_arguments { WTF::move(arguments) }
+    , m_arguments(arguments)
     , m_instructionOffset(0)
 {
     ASSERT(node->op() == TailCall);
@@ -323,6 +342,8 @@
     m_callLinkInfo->setUpCall(m_type, m_semanticeOrigin, calleeGPR);
 }
 
+#endif // FTL_USES_B3
+
 } } // namespace JSC::FTL
 
 #endif // ENABLE(FTL_JIT)
diff --git a/Source/JavaScriptCore/ftl/FTLJSTailCall.h b/Source/JavaScriptCore/ftl/FTLJSTailCall.h
index 5ce6e56..a2d5451 100644
--- a/Source/JavaScriptCore/ftl/FTLJSTailCall.h
+++ b/Source/JavaScriptCore/ftl/FTLJSTailCall.h
@@ -28,6 +28,8 @@
 
 #if ENABLE(FTL_JIT)
 
+#include "B3PatchpointValue.h"
+#include "DFGCommon.h"
 #include "FTLExitValue.h"
 #include "FTLJSCallBase.h"
 #include "FTLStackmapArgumentList.h"
@@ -42,11 +44,21 @@
 
 class JSTailCall : public JSCallBase {
 public:
-    JSTailCall(unsigned stackmapID, DFG::Node*, Vector<ExitValue> arguments);
+    JSTailCall(
+#if FTL_USES_B3
+        B3::PatchpointValue*,
+#else // FTL_USES_B3
+        unsigned stackmapID,
+#endif // FTL_USES_B3
+        DFG::Node*, const Vector<ExitValue>& arguments);
 
     void emit(JITCode&, CCallHelpers&);
-    
+
+#if FTL_USES_B3
+    B3::PatchpointValue* patchpoint() const { return m_patchpoint; }
+#else // FTL_USES_B3
     unsigned stackmapID() const { return m_stackmapID; }
+#endif // FTL_USES_B3
 
     unsigned estimatedSize() const { return m_estimatedSize; }
 
@@ -58,7 +70,11 @@
     }
     
 private:
+#if FTL_USES_B3
+    B3::PatchpointValue* m_patchpoint;
+#else // FTL_USES_B3
     unsigned m_stackmapID;
+#endif // FTL_USES_B3
     Vector<ExitValue> m_arguments;
     unsigned m_estimatedSize;
 
diff --git a/Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp b/Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp
index d1def3b..dbc6336 100644
--- a/Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp
+++ b/Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp
@@ -5374,14 +5374,12 @@
         DFG_ASSERT(m_graph, m_node, m_origin.exitOK);
         
 
-        appendOSRExitDescriptor(UncountableInvalidation, ExceptionType::None, noValue(), nullptr, m_origin);
-        
-        OSRExitDescriptor& exitDescriptor = m_ftlState.jitCode->osrExitDescriptors.last();
+        OSRExitDescriptor* exitDescriptor = appendOSRExitDescriptor(UncountableInvalidation, ExceptionType::None, noValue(), nullptr, m_origin);
         
         StackmapArgumentList arguments = buildExitArguments(exitDescriptor, FormattedValue());
         callStackmap(exitDescriptor, arguments);
         
-        exitDescriptor.m_isInvalidationPoint = true;
+        exitDescriptor->m_isInvalidationPoint = true;
 #endif // FTL_USES_B3
     }
     
@@ -9267,11 +9265,10 @@
         if (!willCatchException)
             return;
 
-        appendOSRExitDescriptor(Uncountable, exceptionType, noValue(), nullptr, m_origin.withForExitAndExitOK(opCatchOrigin, true));
-        OSRExitDescriptor& exitDescriptor = m_ftlState.jitCode->osrExitDescriptors.last();
-        exitDescriptor.m_semanticCodeOriginForCallFrameHeader = codeOriginDescriptionOfCallSite();
-        exitDescriptor.m_baselineExceptionHandler = *exceptionHandler;
-        exitDescriptor.m_stackmapID = m_stackmapIDs - 1;
+        OSRExitDescriptor* exitDescriptor = appendOSRExitDescriptor(Uncountable, exceptionType, noValue(), nullptr, m_origin.withForExitAndExitOK(opCatchOrigin, true));
+        exitDescriptor->m_semanticCodeOriginForCallFrameHeader = codeOriginDescriptionOfCallSite();
+        exitDescriptor->m_baselineExceptionHandler = *exceptionHandler;
+        exitDescriptor->m_stackmapID = m_stackmapIDs - 1;
 
         StackmapArgumentList freshList =
             buildExitArguments(exitDescriptor, noValue(), offsetOfExitArguments);
@@ -9295,9 +9292,9 @@
         return m_blocks.get(block);
     }
 
-    OSRExitDescriptor& appendOSRExitDescriptor(ExitKind kind, ExceptionType exceptionType, FormattedValue lowValue, Node* highValue, NodeOrigin origin)
+    OSRExitDescriptor* appendOSRExitDescriptor(ExitKind kind, ExceptionType exceptionType, FormattedValue lowValue, Node* highValue, NodeOrigin origin)
     {
-        return m_ftlState.jitCode->osrExitDescriptors.alloc(
+        return &m_ftlState.jitCode->osrExitDescriptors.alloc(
             kind, exceptionType, lowValue.format(), m_graph.methodOfGettingAValueProfileFor(highValue),
             origin.forExit, origin.semantic,
             availabilityMap().m_locals.numberOfArguments(),
@@ -9342,7 +9339,7 @@
         blessSpeculation(
             m_out.speculate(failCondition), kind, lowValue, highValue, origin, isExceptionHandler);
 #else // FTL_USES_B3
-        OSRExitDescriptor& exitDescriptor = appendOSRExitDescriptor(kind, isExceptionHandler ? ExceptionType::CCallException : ExceptionType::None, lowValue, highValue, origin);
+        OSRExitDescriptor* exitDescriptor = appendOSRExitDescriptor(kind, isExceptionHandler ? ExceptionType::CCallException : ExceptionType::None, lowValue, highValue, origin);
 
         if (failCondition == m_out.booleanTrue) {
             emitOSRExitCall(exitDescriptor, lowValue);
@@ -9368,26 +9365,32 @@
     }
 
 #if FTL_USES_B3
-    void blessSpeculation(B3::StackmapValue* value, ExitKind kind, FormattedValue lowValue, Node* highValue, NodeOrigin origin, bool isExceptionHandler = false)
+    void blessSpeculation(B3::CheckValue* value, ExitKind kind, FormattedValue lowValue, Node* highValue, NodeOrigin origin, bool isExceptionHandler = false)
     {
-        OSRExitDescriptor& exitDescriptor = appendOSRExitDescriptor(kind, isExceptionHandler ? ExceptionType::CCallException : ExceptionType::None, lowValue, highValue, origin);
+        OSRExitDescriptor* exitDescriptor = appendOSRExitDescriptor(
+            kind, isExceptionHandler ? ExceptionType::CCallException : ExceptionType::None, lowValue,
+            highValue, origin);
+        
+        unsigned offset = value->numChildren();
         value->appendAnys(buildExitArguments(exitDescriptor, lowValue));
+
+        State* state = &m_ftlState;
         value->setGenerator(
-            [&] (CCallHelpers& jit, const B3::StackmapGenerationParams&) {
-                jit.oops();
+            [=] (CCallHelpers& jit, const B3::StackmapGenerationParams& params) {
+                exitDescriptor->emitOSRExit(*state, jit, params, offset);
             });
     }
 #endif
 
 #if !FTL_USES_B3
-    void emitOSRExitCall(OSRExitDescriptor& exitDescriptor, FormattedValue lowValue)
+    void emitOSRExitCall(OSRExitDescriptor* exitDescriptor, FormattedValue lowValue)
     {
         callStackmap(exitDescriptor, buildExitArguments(exitDescriptor, lowValue));
     }
 #endif
 
     StackmapArgumentList buildExitArguments(
-        OSRExitDescriptor& exitDescriptor, FormattedValue lowValue,
+        OSRExitDescriptor* exitDescriptor, FormattedValue lowValue,
         unsigned offsetOfExitArgumentsInStackmapLocations = 0)
     {
         StackmapArgumentList result;
@@ -9397,14 +9400,14 @@
     }
     
     void buildExitArguments(
-        OSRExitDescriptor& exitDescriptor, StackmapArgumentList& arguments, FormattedValue lowValue,
+        OSRExitDescriptor* exitDescriptor, StackmapArgumentList& arguments, FormattedValue lowValue,
         unsigned offsetOfExitArgumentsInStackmapLocations = 0)
     {
         if (!!lowValue)
             arguments.append(lowValue.value());
         
         AvailabilityMap availabilityMap = this->availabilityMap();
-        availabilityMap.pruneByLiveness(m_graph, exitDescriptor.m_codeOrigin);
+        availabilityMap.pruneByLiveness(m_graph, exitDescriptor->m_codeOrigin);
         
         HashMap<Node*, ExitTimeObjectMaterialization*> map;
         availabilityMap.forEachAvailability(
@@ -9419,24 +9422,24 @@
                 auto result = map.add(node, nullptr);
                 if (result.isNewEntry) {
                     result.iterator->value =
-                        exitDescriptor.m_materializations.add(node->op(), node->origin.semantic);
+                        exitDescriptor->m_materializations.add(node->op(), node->origin.semantic);
                 }
             });
         
-        for (unsigned i = 0; i < exitDescriptor.m_values.size(); ++i) {
-            int operand = exitDescriptor.m_values.operandForIndex(i);
+        for (unsigned i = 0; i < exitDescriptor->m_values.size(); ++i) {
+            int operand = exitDescriptor->m_values.operandForIndex(i);
             
             Availability availability = availabilityMap.m_locals[i];
             
             if (Options::validateFTLOSRExitLiveness()) {
                 DFG_ASSERT(
                     m_graph, m_node,
-                    (!(availability.isDead() && m_graph.isLiveInBytecode(VirtualRegister(operand), exitDescriptor.m_codeOrigin))) || m_graph.m_plan.mode == FTLForOSREntryMode);
+                    (!(availability.isDead() && m_graph.isLiveInBytecode(VirtualRegister(operand), exitDescriptor->m_codeOrigin))) || m_graph.m_plan.mode == FTLForOSREntryMode);
             }
             ExitValue exitValue = exitValueForAvailability(arguments, map, availability);
             if (exitValue.hasIndexInStackmapLocations())
                 exitValue.adjustStackmapLocationsIndexByOffset(offsetOfExitArgumentsInStackmapLocations);
-            exitDescriptor.m_values[i] = exitValue;
+            exitDescriptor->m_values[i] = exitValue;
         }
         
         for (auto heapPair : availabilityMap.m_heap) {
@@ -9451,21 +9454,21 @@
         }
         
         if (verboseCompilationEnabled()) {
-            dataLog("        Exit values: ", exitDescriptor.m_values, "\n");
-            if (!exitDescriptor.m_materializations.isEmpty()) {
+            dataLog("        Exit values: ", exitDescriptor->m_values, "\n");
+            if (!exitDescriptor->m_materializations.isEmpty()) {
                 dataLog("        Materializations: \n");
-                for (ExitTimeObjectMaterialization* materialization : exitDescriptor.m_materializations)
+                for (ExitTimeObjectMaterialization* materialization : exitDescriptor->m_materializations)
                     dataLog("            ", pointerDump(materialization), "\n");
             }
         }
     }
 
 #if !FTL_USES_B3
-    void callStackmap(OSRExitDescriptor& exitDescriptor, StackmapArgumentList arguments)
+    void callStackmap(OSRExitDescriptor* exitDescriptor, StackmapArgumentList arguments)
     {
-        exitDescriptor.m_stackmapID = m_stackmapIDs++;
+        exitDescriptor->m_stackmapID = m_stackmapIDs++;
         arguments.insert(0, m_out.constInt32(MacroAssembler::maxJumpReplacementSize()));
-        arguments.insert(0, m_out.constInt64(exitDescriptor.m_stackmapID));
+        arguments.insert(0, m_out.constInt64(exitDescriptor->m_stackmapID));
         
         m_out.call(m_out.voidType, m_out.stackmapIntrinsic(), arguments);
     }
@@ -9929,10 +9932,6 @@
 
 void lowerDFGToLLVM(State& state)
 {
-#if FTL_USES_B3
-    state.proc = std::make_unique<Procedure>();
-#endif
-
     LowerDFGToLLVM lowering(state);
     lowering.lower();
 }
diff --git a/Source/JavaScriptCore/ftl/FTLOSRExit.cpp b/Source/JavaScriptCore/ftl/FTLOSRExit.cpp
index 3194da5..5aba1f6 100644
--- a/Source/JavaScriptCore/ftl/FTLOSRExit.cpp
+++ b/Source/JavaScriptCore/ftl/FTLOSRExit.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2013, 2015 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -28,16 +28,20 @@
 
 #if ENABLE(FTL_JIT)
 
+#include "AirGenerationContext.h"
+#include "B3StackmapValue.h"
 #include "CodeBlock.h"
 #include "DFGBasicBlock.h"
 #include "DFGNode.h"
 #include "FTLExitArgument.h"
 #include "FTLJITCode.h"
 #include "FTLLocation.h"
+#include "FTLState.h"
 #include "JSCInlines.h"
 
 namespace JSC { namespace FTL {
 
+using namespace B3;
 using namespace DFG;
 
 OSRExitDescriptor::OSRExitDescriptor(
@@ -70,23 +74,61 @@
         materialization->validateReferences(trackedReferences);
 }
 
-
-OSRExit::OSRExit(OSRExitDescriptor& descriptor, uint32_t stackmapRecordIndex)
-    : OSRExitBase(descriptor.m_kind, descriptor.m_codeOrigin, descriptor.m_codeOriginForExitProfile)
-    , m_descriptor(descriptor)
-    , m_stackmapRecordIndex(stackmapRecordIndex)
-    , m_exceptionType(descriptor.m_exceptionType)
+#if FTL_USES_B3
+RefPtr<OSRExitHandle> OSRExitDescriptor::emitOSRExit(
+    State& state, CCallHelpers& jit, const StackmapGenerationParams& params, unsigned offset)
 {
-    m_isExceptionHandler = descriptor.isExceptionHandler();
+    RefPtr<OSRExitHandle> handle = prepareOSRExitHandle(state, params, offset);
+    handle->emitExitThunk(jit);
+    return handle;
+}
+
+RefPtr<OSRExitHandle> OSRExitDescriptor::emitOSRExitLater(
+    State& state, const StackmapGenerationParams& params, unsigned offset)
+{
+    RefPtr<OSRExitHandle> handle = prepareOSRExitHandle(state, params, offset);
+    params.context->latePaths.append(
+        createSharedTask<Air::GenerationContext::LatePathFunction>(
+            [handle] (CCallHelpers& jit, Air::GenerationContext&) {
+                handle->emitExitThunk(jit);
+            }));
+    return handle;
+}
+
+RefPtr<OSRExitHandle> OSRExitDescriptor::prepareOSRExitHandle(
+    State& state, const StackmapGenerationParams& params, unsigned offset)
+{
+    unsigned index = state.jitCode->osrExit.size();
+    RefPtr<OSRExitHandle> handle = adoptRef(
+        new OSRExitHandle(index, state.jitCode->osrExit.alloc(this)));
+    for (unsigned i = offset; i < params.reps.size(); ++i)
+        handle->exit.m_valueReps.append(params.reps[i]);
+    handle->exit.m_valueReps.shrinkToFit();
+    return handle;
+}
+#endif // FTL_USES_B3
+
+OSRExit::OSRExit(
+    OSRExitDescriptor* descriptor
+#if !FTL_USES_B3
+    , uint32_t stackmapRecordIndex
+#endif // !FTL_USES_B3
+    )
+    : OSRExitBase(descriptor->m_kind, descriptor->m_codeOrigin, descriptor->m_codeOriginForExitProfile)
+    , m_descriptor(descriptor)
+#if !FTL_USES_B3
+    , m_stackmapRecordIndex(stackmapRecordIndex)
+#endif // !FTL_USES_B3
+    , m_exceptionType(descriptor->m_exceptionType)
+{
+    m_isExceptionHandler = descriptor->isExceptionHandler();
 }
 
 CodeLocationJump OSRExit::codeLocationForRepatch(CodeBlock* ftlCodeBlock) const
 {
 #if FTL_USES_B3
-    return CodeLocationJump(
-        reinterpret_cast<char*>(
-            ftlCodeBlock->jitCode()->ftl()->b3Code().code().dataLocation()) +
-        m_patchableCodeOffset);
+    UNUSED_PARAM(ftlCodeBlock);
+    return m_patchableJump;
 #else // FTL_USES_B3
     return CodeLocationJump(
         reinterpret_cast<char*>(
@@ -95,9 +137,10 @@
 #endif // FTL_USES_B3
 }
 
+#if !FTL_USES_B3
 void OSRExit::gatherRegistersToSpillForCallIfException(StackMaps& stackmaps, StackMaps::Record& record)
 {
-    RELEASE_ASSERT(m_descriptor.m_exceptionType == ExceptionType::JSCall);
+    RELEASE_ASSERT(m_descriptor->m_exceptionType == ExceptionType::JSCall);
 
     RegisterSet volatileRegisters = RegisterSet::volatileRegistersForJSCall();
 
@@ -121,12 +164,12 @@
             break;
         }
     };
-    for (ExitTimeObjectMaterialization* materialization : m_descriptor.m_materializations) {
+    for (ExitTimeObjectMaterialization* materialization : m_descriptor->m_materializations) {
         for (unsigned propertyIndex = materialization->properties().size(); propertyIndex--;)
             addNeededRegisters(materialization->properties()[propertyIndex].value());
     }
-    for (unsigned index = m_descriptor.m_values.size(); index--;)
-        addNeededRegisters(m_descriptor.m_values[index]);
+    for (unsigned index = m_descriptor->m_values.size(); index--;)
+        addNeededRegisters(m_descriptor->m_values[index]);
 }
 
 void OSRExit::spillRegistersToSpillSlot(CCallHelpers& jit, int32_t stackSpillSlot)
@@ -164,6 +207,7 @@
         }
     }
 }
+#endif // !FTL_USES_B3
 
 bool OSRExit::willArriveAtExitFromIndirectExceptionCheck() const
 {
diff --git a/Source/JavaScriptCore/ftl/FTLOSRExit.h b/Source/JavaScriptCore/ftl/FTLOSRExit.h
index d1341f9..0b35cbb 100644
--- a/Source/JavaScriptCore/ftl/FTLOSRExit.h
+++ b/Source/JavaScriptCore/ftl/FTLOSRExit.h
@@ -28,6 +28,7 @@
 
 #if ENABLE(FTL_JIT)
 
+#include "B3ValueRep.h"
 #include "CodeOrigin.h"
 #include "DFGExitProfile.h"
 #include "DFGOSRExitBase.h"
@@ -35,6 +36,7 @@
 #include "FTLExitTimeObjectMaterialization.h"
 #include "FTLExitValue.h"
 #include "FTLFormattedValue.h"
+#include "FTLOSRExitHandle.h"
 #include "FTLStackMaps.h"
 #include "FTLStackmapArgumentList.h"
 #include "HandlerInfo.h"
@@ -48,93 +50,16 @@
 
 class TrackedReferences;
 
+namespace B3 {
+struct StackmapGenerationParams;
+namespace Air {
+struct GenerationContext;
+} // namespace Air
+} // namespace B3
+
 namespace FTL {
 
-// Tracks one OSR exit site within the FTL JIT. OSR exit in FTL works by deconstructing
-// the crazy that is OSR down to simple SSA CFG primitives that any compiler backend
-// (including of course LLVM) can grok and do meaningful things to. An exit is just a
-// conditional branch in the emitted code where one destination is the continuation and
-// the other is a basic block that performs a no-return tail-call to an  exit thunk.
-// This thunk takes as its arguments the live non-constant not-already-accounted-for
-// bytecode state. To appreciate how this works consider the following JavaScript
-// program, and its lowering down to LLVM IR including the relevant exits:
-//
-// function foo(o) {
-//     var a = o.a; // predicted int
-//     var b = o.b;
-//     var c = o.c; // NB this is dead
-//     a = a | 5; // our example OSR exit: need to check if a is an int
-//     return a + b;
-// }
-//
-// Just consider the "a | 5". In the DFG IR, this looks like:
-//
-// BitOr(Check:Int32:@a, Int32:5)
-//
-// Where @a is the node for the value of the 'a' variable. Conceptually, this node can
-// be further broken down to the following (note that this particular lowering never
-// actually happens - we skip this step and go straight to LLVM IR - but it's still
-// useful to see this):
-//
-// exitIf(@a is not int32);
-// continuation;
-//
-// Where 'exitIf()' is a function that will exit if the argument is true, and
-// 'continuation' is the stuff that we will do after the exitIf() check. (Note that
-// FTL refers to 'exitIf()' as 'speculate()', which is in line with DFG terminology.)
-// This then gets broken down to the following LLVM IR, assuming that %0 is the LLVM
-// value corresponding to variable 'a', and %1 is the LLVM value for variable 'b':
-//
-//   %2 = ... // the predictate corresponding to '@a is not int32'
-//   br i1 %2, label %3, label %4
-// ; <label>:3
-//   call void exitThunk1(%0, %1) // pass 'a' and 'b', since they're both live-in-bytecode
-//   unreachable
-// ; <label>:4
-//   ... // code for the continuation
-//
-// Where 'exitThunk1' is the IR to get the exit thunk for *this* OSR exit. Each OSR
-// exit will appear to LLVM to have a distinct exit thunk.
-//
-// Note that this didn't have to pass '5', 'o', or 'c' to the exit thunk. 5 is a
-// constant and the DFG already knows that, and can already tell the OSR exit machinery
-// what that contant is and which bytecode variables (if any) it needs to be dropped
-// into. This is conveyed to the exit statically, via the OSRExit data structure below.
-// See the code for ExitValue for details. 'o' is an argument, and arguments are always
-// "flushed" - if you never assign them then their values are still in the argument
-// stack slots, and if you do assign them then we eagerly store them into those slots.
-// 'c' is dead in bytecode, and the DFG knows this; we statically tell the exit thunk
-// that it's dead and don't have to pass anything. The exit thunk will "initialize" its
-// value to Undefined.
-//
-// This approach to OSR exit has a number of virtues:
-//
-// - It is an entirely unsurprising representation for a compiler that already groks
-//   CFG-like IRs for C-like languages. All existing analyses and transformations just
-//   work.
-//
-// - It lends itself naturally to modern approaches to code motion. For example, you
-//   could sink operations from above the exit to below it, if you just duplicate the
-//   operation into the OSR exit block. This is both legal and desirable. It works
-//   because the backend sees the OSR exit block as being no different than any other,
-//   and LLVM already supports sinking if it sees that a value is only partially used.
-//   Hence there exists a value that dominates the exit but is only used by the exit
-//   thunk and not by the continuation, sinking ought to kick in for that value.
-//   Hoisting operations from below it to above it is also possible, for similar
-//   reasons.
-//
-// - The no-return tail-call to the OSR exit thunk can be subjected to specialized
-//   code-size reduction optimizations, though this is optional. For example, instead
-//   of actually emitting a call along with all that goes with it (like placing the
-//   arguments into argument position), the backend could choose to simply inform us
-//   where it had placed the arguments and expect the callee (i.e. the exit thunk) to
-//   figure it out from there. It could also tell us what we need to do to pop stack,
-//   although again, it doesn't have to; it could just emit that code normally. We do
-//   all of these things through the patchpoint/stackmap LLVM intrinsics.
-//
-// - It could be extended to allow the backend to do its own exit hoisting, by using
-//   intrinsics (or meta-data, or something) to inform the backend that it's safe to
-//   make the predicate passed to 'exitIf()' more truthy.
+class State;
 
 enum class ExceptionType : uint8_t {
     None,
@@ -175,20 +100,59 @@
     
     uint32_t m_stackmapID;
     HandlerInfo m_baselineExceptionHandler;
-    bool m_isInvalidationPoint : 1;
+    bool m_isInvalidationPoint;
     
     void validateReferences(const TrackedReferences&);
+
+#if FTL_USES_B3
+    // Call this once we have a place to emit the OSR exit jump and we have data about how the state
+    // should be recovered. This effectively emits code that does the exit, though the code is really a
+    // patchable jump and we emit the real code lazily. The description of how to emit the real code is
+    // up to the OSRExit object, which this creates. Note that it's OK to drop the OSRExitHandle object
+    // on the ground. It contains information that is mostly not useful if you use this API, since after
+    // this call, the OSRExit is simply ready to go.
+    RefPtr<OSRExitHandle> emitOSRExit(
+        State&, CCallHelpers&, const B3::StackmapGenerationParams&, unsigned offset);
+
+    // In some cases you want an OSRExit to come into existence, but you don't want to emit it right now.
+    // This will emit the OSR exit in a late path. You can't be sure exactly when that will happen, but
+    // you know that it will be done by the time late path emission is done. So, a linker task will
+    // surely happen after that. You can use the OSRExitHandle to retrieve the exit's label.
+    //
+    // This API is meant to be used for things like exception handling, where some patchpoint wants to
+    // have a place to jump to for OSR exit. It doesn't care where that OSR exit is emitted so long as it
+    // eventually gets access to its label.
+    RefPtr<OSRExitHandle> emitOSRExitLater(
+        State&, const B3::StackmapGenerationParams&, unsigned offset);
+
+    // This is the low-level interface. It will create a handle representing the desire to emit code for
+    // an OSR exit. You can call OSRExitHandle::emitExitThunk() once you have a place to emit it. Note
+    // that the above two APIs are written in terms of this and OSRExitHandle::emitExitThunk().
+    RefPtr<OSRExitHandle> prepareOSRExitHandle(
+        State&, const B3::StackmapGenerationParams&, unsigned offset);
+#endif // FTL_USES_B3
 };
 
 struct OSRExit : public DFG::OSRExitBase {
-    OSRExit(OSRExitDescriptor&, uint32_t stackmapRecordIndex);
+    OSRExit(
+        OSRExitDescriptor*
+#if !FTL_USES_B3
+        , uint32_t stackmapRecordIndex
+#endif // !FTL_USES_B3
+        );
 
-    OSRExitDescriptor& m_descriptor;
+    OSRExitDescriptor* m_descriptor;
     MacroAssemblerCodeRef m_code;
+#if FTL_USES_B3
+    // This tells us where to place a jump.
+    CodeLocationJump m_patchableJump;
+    Vector<B3::ValueRep> m_valueReps;
+#else // FTL_USES_B3
     // Offset within the exit stubs of the stub for this exit.
     unsigned m_patchableCodeOffset;
     // Offset within Stackmap::records
     uint32_t m_stackmapRecordIndex;
+#endif // FTL_USES_B3
     ExceptionType m_exceptionType;
 
     RegisterSet registersToPreserveForCallThatMightThrow;
@@ -199,9 +163,11 @@
         OSRExitBase::considerAddingAsFrequentExitSite(profiledCodeBlock, ExitFromFTL);
     }
 
+#if !FTL_USES_B3
     void gatherRegistersToSpillForCallIfException(StackMaps&, StackMaps::Record&);
     void spillRegistersToSpillSlot(CCallHelpers&, int32_t stackSpillSlot);
     void recoverRegistersFromSpillSlot(CCallHelpers& jit, int32_t stackSpillSlot);
+#endif // !FTL_USES_B3
 
     bool willArriveAtOSRExitFromGenericUnwind() const;
     bool willArriveAtExitFromIndirectExceptionCheck() const;
diff --git a/Source/JavaScriptCore/ftl/FTLOSRExitCompilationInfo.h b/Source/JavaScriptCore/ftl/FTLOSRExitCompilationInfo.h
index e47183d..ecefc01 100644
--- a/Source/JavaScriptCore/ftl/FTLOSRExitCompilationInfo.h
+++ b/Source/JavaScriptCore/ftl/FTLOSRExitCompilationInfo.h
@@ -26,7 +26,9 @@
 #ifndef FTLOSRExitCompilationInfo_h
 #define FTLOSRExitCompilationInfo_h
 
-#if ENABLE(FTL_JIT)
+#include "DFGCommon.h"
+
+#if ENABLE(FTL_JIT) && !FTL_USES_B3
 
 #include "FTLAbbreviatedTypes.h"
 #include "MacroAssembler.h"
@@ -37,7 +39,7 @@
     OSRExitCompilationInfo()
     {
     }
-    
+
     MacroAssembler::Label m_thunkLabel;
     MacroAssembler::PatchableJump m_thunkJump;
     CodeLocationLabel m_thunkAddress;
@@ -45,7 +47,7 @@
 
 } } // namespace JSC::FTL
 
-#endif // ENABLE(FTL_JIT)
+#endif // ENABLE(FTL_JIT) && !FTL_USES_B3
 
 #endif // FTLOSRExitCompilationInfo_h
 
diff --git a/Source/JavaScriptCore/ftl/FTLOSRExitCompiler.cpp b/Source/JavaScriptCore/ftl/FTLOSRExitCompiler.cpp
index a9a707d..f4edfed 100644
--- a/Source/JavaScriptCore/ftl/FTLOSRExitCompiler.cpp
+++ b/Source/JavaScriptCore/ftl/FTLOSRExitCompiler.cpp
@@ -98,7 +98,12 @@
 }
 
 static void compileRecovery(
-    CCallHelpers& jit, const ExitValue& value, StackMaps::Record* record, StackMaps& stackmaps,
+    CCallHelpers& jit, const ExitValue& value,
+#if FTL_USES_B3
+    Vector<B3::ValueRep>& valueReps,
+#else // FTL_USES_B3
+    StackMaps::Record* record, StackMaps& stackmaps,
+#endif // FTL_USES_B3
     char* registerScratch,
     const HashMap<ExitTimeObjectMaterialization*, EncodedJSValue*>& materializationToPointer)
 {
@@ -112,8 +117,13 @@
         break;
             
     case ExitValueArgument:
+#if FTL_USES_B3
+        Location::forValueRep(valueReps[value.exitArgument().argument()]).restoreInto(
+            jit, registerScratch, GPRInfo::regT0);
+#else // FTL_USES_B3
         record->locations[value.exitArgument().argument()].restoreInto(
             jit, stackmaps, registerScratch, GPRInfo::regT0);
+#endif // FTL_USES_B3
         break;
             
     case ExitValueInJSStack:
@@ -124,10 +134,17 @@
         break;
             
     case ExitValueRecovery:
+#if FTL_USES_B3
+        Location::forValueRep(valueReps[value.rightRecoveryArgument()]).restoreInto(
+            jit, registerScratch, GPRInfo::regT1);
+        Location::forValueRep(valueReps[value.leftRecoveryArgument()]).restoreInto(
+            jit, registerScratch, GPRInfo::regT0);
+#else // FTL_USES_B3
         record->locations[value.rightRecoveryArgument()].restoreInto(
             jit, stackmaps, registerScratch, GPRInfo::regT1);
         record->locations[value.leftRecoveryArgument()].restoreInto(
             jit, stackmaps, registerScratch, GPRInfo::regT0);
+#endif // FTL_USES_B3
         switch (value.recoveryOpcode()) {
         case AddRecovery:
             switch (value.recoveryFormat()) {
@@ -177,8 +194,12 @@
 static void compileStub(
     unsigned exitID, JITCode* jitCode, OSRExit& exit, VM* vm, CodeBlock* codeBlock)
 {
+#if FTL_USES_B3
+    UNUSED_PARAM(jitCode);
+#else // FTL_USES_B3
     StackMaps::Record* record = &jitCode->stackmaps.records[exit.m_stackmapRecordIndex];
-    RELEASE_ASSERT(record->patchpointID == exit.m_descriptor.m_stackmapID);
+    RELEASE_ASSERT(record->patchpointID == exit.m_descriptor->m_stackmapID);
+#endif // FTL_USES_B3
 
     // This code requires framePointerRegister is the same as callFrameRegister
     static_assert(MacroAssembler::framePointerRegister == GPRInfo::callFrameRegister, "MacroAssembler::framePointerRegister and GPRInfo::callFrameRegister must be the same");
@@ -192,7 +213,7 @@
     // Figure out how much space we need for those object allocations.
     unsigned numMaterializations = 0;
     size_t maxMaterializationNumArguments = 0;
-    for (ExitTimeObjectMaterialization* materialization : exit.m_descriptor.m_materializations) {
+    for (ExitTimeObjectMaterialization* materialization : exit.m_descriptor->m_materializations) {
         numMaterializations++;
         
         maxMaterializationNumArguments = std::max(
@@ -202,21 +223,32 @@
     
     ScratchBuffer* scratchBuffer = vm->scratchBufferForSize(
         sizeof(EncodedJSValue) * (
-            exit.m_descriptor.m_values.size() + numMaterializations + maxMaterializationNumArguments) +
+            exit.m_descriptor->m_values.size() + numMaterializations + maxMaterializationNumArguments) +
         requiredScratchMemorySizeInBytes() +
         codeBlock->calleeSaveRegisters()->size() * sizeof(uint64_t));
     EncodedJSValue* scratch = scratchBuffer ? static_cast<EncodedJSValue*>(scratchBuffer->dataBuffer()) : 0;
-    EncodedJSValue* materializationPointers = scratch + exit.m_descriptor.m_values.size();
+    EncodedJSValue* materializationPointers = scratch + exit.m_descriptor->m_values.size();
     EncodedJSValue* materializationArguments = materializationPointers + numMaterializations;
     char* registerScratch = bitwise_cast<char*>(materializationArguments + maxMaterializationNumArguments);
     uint64_t* unwindScratch = bitwise_cast<uint64_t*>(registerScratch + requiredScratchMemorySizeInBytes());
     
     HashMap<ExitTimeObjectMaterialization*, EncodedJSValue*> materializationToPointer;
     unsigned materializationCount = 0;
-    for (ExitTimeObjectMaterialization* materialization : exit.m_descriptor.m_materializations) {
+    for (ExitTimeObjectMaterialization* materialization : exit.m_descriptor->m_materializations) {
         materializationToPointer.add(
             materialization, materializationPointers + materializationCount++);
     }
+
+    auto recoverValue = [&] (const ExitValue& value) {
+        compileRecovery(
+            jit, value,
+#if FTL_USES_B3
+            exit.m_valueReps,
+#else // FTL_USES_B3
+            record, jitCode->stackmaps,
+#endif // FTL_USES_B3
+            registerScratch, materializationToPointer);
+    };
     
     // Note that we come in here, the stack used to be as LLVM left it except that someone called pushToSave().
     // We don't care about the value they saved. But, we do appreciate the fact that they did it, because we use
@@ -247,10 +279,14 @@
     jit.move(MacroAssembler::TrustedImm64(TagMask), GPRInfo::tagMaskRegister);
     
     // Do some value profiling.
-    if (exit.m_descriptor.m_profileDataFormat != DataFormatNone) {
+    if (exit.m_descriptor->m_profileDataFormat != DataFormatNone) {
+#if FTL_USES_B3
+        Location::forValueRep(exit.m_valueReps[0]).restoreInto(jit, registerScratch, GPRInfo::regT0);
+#else // FTL_USES_B3
         record->locations[0].restoreInto(jit, jitCode->stackmaps, registerScratch, GPRInfo::regT0);
+#endif // FTL_USES_B3
         reboxAccordingToFormat(
-            exit.m_descriptor.m_profileDataFormat, jit, GPRInfo::regT0, GPRInfo::regT1, GPRInfo::regT2);
+            exit.m_descriptor->m_profileDataFormat, jit, GPRInfo::regT0, GPRInfo::regT1, GPRInfo::regT2);
         
         if (exit.m_kind == BadCache || exit.m_kind == BadIndexingType) {
             CodeOrigin codeOrigin = exit.m_codeOriginForExitProfile;
@@ -264,8 +300,8 @@
             }
         }
 
-        if (!!exit.m_descriptor.m_valueProfile)
-            jit.store64(GPRInfo::regT0, exit.m_descriptor.m_valueProfile.getSpecFailBucket(0));
+        if (!!exit.m_descriptor->m_valueProfile)
+            jit.store64(GPRInfo::regT0, exit.m_descriptor->m_valueProfile.getSpecFailBucket(0));
     }
 
     // Materialize all objects. Don't materialize an object until all
@@ -275,7 +311,7 @@
     // allocation of the former.
 
     HashSet<ExitTimeObjectMaterialization*> toMaterialize;
-    for (ExitTimeObjectMaterialization* materialization : exit.m_descriptor.m_materializations)
+    for (ExitTimeObjectMaterialization* materialization : exit.m_descriptor->m_materializations)
         toMaterialize.add(materialization);
 
     while (!toMaterialize.isEmpty()) {
@@ -307,13 +343,10 @@
             // We only recover the fields that are needed for the allocation.
             for (unsigned propertyIndex = materialization->properties().size(); propertyIndex--;) {
                 const ExitPropertyValue& property = materialization->properties()[propertyIndex];
-                const ExitValue& value = property.value();
                 if (!property.location().neededForMaterialization())
                     continue;
 
-                compileRecovery(
-                    jit, value, record, jitCode->stackmaps, registerScratch,
-                    materializationToPointer);
+                recoverValue(property.value());
                 jit.storePtr(GPRInfo::regT0, materializationArguments + propertyIndex);
             }
             
@@ -338,12 +371,9 @@
     // Now that all the objects have been allocated, we populate them
     // with the correct values. This time we can recover all the
     // fields, including those that are only needed for the allocation.
-    for (ExitTimeObjectMaterialization* materialization : exit.m_descriptor.m_materializations) {
+    for (ExitTimeObjectMaterialization* materialization : exit.m_descriptor->m_materializations) {
         for (unsigned propertyIndex = materialization->properties().size(); propertyIndex--;) {
-            const ExitValue& value = materialization->properties()[propertyIndex].value();
-            compileRecovery(
-                jit, value, record, jitCode->stackmaps, registerScratch,
-                materializationToPointer);
+            recoverValue(materialization->properties()[propertyIndex].value());
             jit.storePtr(GPRInfo::regT0, materializationArguments + propertyIndex);
         }
 
@@ -359,16 +389,14 @@
     // Save all state from wherever the exit data tells us it was, into the appropriate place in
     // the scratch buffer. This also does the reboxing.
     
-    for (unsigned index = exit.m_descriptor.m_values.size(); index--;) {
-        compileRecovery(
-            jit, exit.m_descriptor.m_values[index], record, jitCode->stackmaps, registerScratch,
-            materializationToPointer);
+    for (unsigned index = exit.m_descriptor->m_values.size(); index--;) {
+        recoverValue(exit.m_descriptor->m_values[index]);
         jit.store64(GPRInfo::regT0, scratch + index);
     }
     
     // Henceforth we make it look like the exiting function was called through a register
     // preservation wrapper. This implies that FP must be nudged down by a certain amount. Then
-    // we restore the various things according to either exit.m_descriptor.m_values or by copying from the
+    // we restore the various things according to either exit.m_descriptor->m_values or by copying from the
     // old frame, and finally we save the various callee-save registers into where the
     // restoration thunk would restore them from.
     
@@ -416,7 +444,7 @@
 
     // First set up SP so that our data doesn't get clobbered by signals.
     unsigned conservativeStackDelta =
-        (exit.m_descriptor.m_values.numberOfLocals() + baselineCodeBlock->calleeSaveSpaceAsVirtualRegisters()) * sizeof(Register) +
+        (exit.m_descriptor->m_values.numberOfLocals() + baselineCodeBlock->calleeSaveSpaceAsVirtualRegisters()) * sizeof(Register) +
         maxFrameExtentForSlowPathCall;
     conservativeStackDelta = WTF::roundUpToMultipleOf(
         stackAlignmentBytes(), conservativeStackDelta);
@@ -493,8 +521,8 @@
 
     // Now get state out of the scratch buffer and place it back into the stack. The values are
     // already reboxed so we just move them.
-    for (unsigned index = exit.m_descriptor.m_values.size(); index--;) {
-        VirtualRegister reg = exit.m_descriptor.m_values.virtualRegisterForIndex(index);
+    for (unsigned index = exit.m_descriptor->m_values.size(); index--;) {
+        VirtualRegister reg = exit.m_descriptor->m_values.virtualRegisterForIndex(index);
 
         if (reg.isLocal() && reg.toLocal() < static_cast<int>(baselineVirtualRegistersForCalleeSaves))
             continue;
@@ -511,11 +539,19 @@
     exit.m_code = FINALIZE_CODE_IF(
         shouldDumpDisassembly() || Options::verboseOSR() || Options::verboseFTLOSRExit(),
         patchBuffer,
+#if FTL_USES_B3
+        ("FTL OSR exit #%u (%s, %s) from %s, with operands = %s",
+            exitID, toCString(exit.m_codeOrigin).data(),
+            exitKindToString(exit.m_kind), toCString(*codeBlock).data(),
+            toCString(ignoringContext<DumpContext>(exit.m_descriptor->m_values)).data())
+#else // FTL_USES_B3
         ("FTL OSR exit #%u (%s, %s) from %s, with operands = %s, and record = %s",
             exitID, toCString(exit.m_codeOrigin).data(),
             exitKindToString(exit.m_kind), toCString(*codeBlock).data(),
-            toCString(ignoringContext<DumpContext>(exit.m_descriptor.m_values)).data(),
-            toCString(*record).data()));
+            toCString(ignoringContext<DumpContext>(exit.m_descriptor->m_values)).data(),
+            toCString(*record).data())
+#endif // FTL_USES_B3
+        );
 }
 
 extern "C" void* compileFTLOSRExit(ExecState* exec, unsigned exitID)
@@ -544,15 +580,15 @@
         dataLog("    Origin: ", exit.m_codeOrigin, "\n");
         if (exit.m_codeOriginForExitProfile != exit.m_codeOrigin)
             dataLog("    Origin for exit profile: ", exit.m_codeOriginForExitProfile, "\n");
-        dataLog("    Exit stackmap ID: ", exit.m_descriptor.m_stackmapID, "\n");
+        dataLog("    Exit stackmap ID: ", exit.m_descriptor->m_stackmapID, "\n");
         dataLog("    Current call site index: ", exec->callSiteIndex().bits(), "\n");
         dataLog("    Exit is exception handler: ", exit.m_isExceptionHandler,
             " will arrive at exit from genericUnwind(): ", exit.willArriveAtOSRExitFromGenericUnwind(), 
-            " will arrive at exit from lazy slow path: ", exit.m_descriptor.m_exceptionType == ExceptionType::LazySlowPath, "\n");
-        dataLog("    Exit values: ", exit.m_descriptor.m_values, "\n");
-        if (!exit.m_descriptor.m_materializations.isEmpty()) {
+            " will arrive at exit from lazy slow path: ", exit.m_descriptor->m_exceptionType == ExceptionType::LazySlowPath, "\n");
+        dataLog("    Exit values: ", exit.m_descriptor->m_values, "\n");
+        if (!exit.m_descriptor->m_materializations.isEmpty()) {
             dataLog("    Materializations:\n");
-            for (ExitTimeObjectMaterialization* materialization : exit.m_descriptor.m_materializations)
+            for (ExitTimeObjectMaterialization* materialization : exit.m_descriptor->m_materializations)
                 dataLog("        ", pointerDump(materialization), "\n");
         }
     }
diff --git a/Source/JavaScriptCore/ftl/FTLOSRExitHandle.cpp b/Source/JavaScriptCore/ftl/FTLOSRExitHandle.cpp
new file mode 100644
index 0000000..6152523
--- /dev/null
+++ b/Source/JavaScriptCore/ftl/FTLOSRExitHandle.cpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#include "config.h"
+#include "FTLOSRExitHandle.h"
+
+#if ENABLE(FTL_JIT) && FTL_USES_B3
+
+#include "FTLOSRExit.h"
+#include "FTLThunks.h"
+#include "LinkBuffer.h"
+
+namespace JSC { namespace FTL {
+
+void OSRExitHandle::emitExitThunk(CCallHelpers& jit)
+{
+    label = jit.label();
+    jit.pushToSaveImmediateWithoutTouchingRegisters(CCallHelpers::TrustedImm32(index));
+    CCallHelpers::PatchableJump jump = jit.patchableJump();
+    jit.addLinkTask(
+        [this, jump] (LinkBuffer& linkBuffer) {
+            exit.m_patchableJump = CodeLocationJump(linkBuffer.locationOf(jump));
+
+            linkBuffer.link(
+                jump.m_jump,
+                CodeLocationLabel(linkBuffer.vm().getCTIStub(osrExitGenerationThunkGenerator).code()));
+        });
+}
+
+} } // namespace JSC::FTL
+
+#endif // ENABLE(FTL_JIT) && FTL_USES_B3
+
diff --git a/Source/JavaScriptCore/ftl/FTLOSRExitHandle.h b/Source/JavaScriptCore/ftl/FTLOSRExitHandle.h
new file mode 100644
index 0000000..2725fa4
--- /dev/null
+++ b/Source/JavaScriptCore/ftl/FTLOSRExitHandle.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#ifndef FTLOSRExitHandle_h
+#define FTLOSRExitHandle_h
+
+#include "DFGCommon.h"
+
+#if ENABLE(FTL_JIT) && FTL_USES_B3
+
+#include "CCallHelpers.h"
+#include <wtf/ThreadSafeRefCounted.h>
+
+namespace JSC { namespace FTL {
+
+struct OSRExit;
+
+// This is an object that stores some interesting data about an OSR exit. It's expected that you will
+// scrape this data from this object by the time compilation finishes.
+struct OSRExitHandle : public ThreadSafeRefCounted<OSRExitHandle> {
+    OSRExitHandle(unsigned index, OSRExit& exit)
+        : index(index)
+        , exit(exit)
+    {
+    }
+
+    unsigned index;
+    OSRExit& exit;
+
+    // This is the label at which the OSR exit jump lives. This will get populated once the OSR exit
+    // emits its jump. This happens immediately when you call OSRExit::appendOSRExit(). It happens at
+    // some time during late path emission if you do OSRExit::appendOSRExitLater().
+    CCallHelpers::Label label;
+
+    // This emits the exit thunk and populates 'label'.
+    void emitExitThunk(CCallHelpers&);
+};
+
+} } // namespace JSC::FTL
+
+#endif // ENABLE(FTL_JIT) && FTL_USES_B3
+
+#endif // FTLOSRExitHandle_h
+
diff --git a/Source/JavaScriptCore/ftl/FTLState.cpp b/Source/JavaScriptCore/ftl/FTLState.cpp
index 55d829a..3cbb5b6 100644
--- a/Source/JavaScriptCore/ftl/FTLState.cpp
+++ b/Source/JavaScriptCore/ftl/FTLState.cpp
@@ -38,6 +38,7 @@
 
 namespace JSC { namespace FTL {
 
+using namespace B3;
 using namespace DFG;
 
 State::State(Graph& graph)
@@ -68,6 +69,10 @@
 
     graph.m_plan.finalizer = std::make_unique<JITFinalizer>(graph.m_plan);
     finalizer = static_cast<JITFinalizer*>(graph.m_plan.finalizer.get());
+
+#if FTL_USES_B3
+    proc = std::make_unique<Procedure>();
+#endif // FTL_USES_B3
 }
 
 State::~State()