<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[192439] trunk/Source/JavaScriptCore</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/192439">192439</a></dd>
<dt>Author</dt> <dd>sbarati@apple.com</dd>
<dt>Date</dt> <dd>2015-11-13 13:36:18 -0800 (Fri, 13 Nov 2015)</dd>
</dl>

<h3>Log Message</h3>
<pre>sub IC does not properly handle exception handling now that try/catch is compiled in the FTL
https://bugs.webkit.org/show_bug.cgi?id=151080

Reviewed by Geoffrey Garen.

This patch implements proper exception handling for ArithSubs with binaryUseKind as
UntypedUse inside try/catch.  We implement this in a very similar way as we implement
exception handling from GetById/PutById/LazySlowPaths callOperation calls. We do the needed
spilling if the result is the same register as the left/right register because in the event
of an exception, we don't want to do value recovery on the garbage result, we
want to do it on the original lhs/rhs of the operation.

This patch also does some refactoring. No longer does OSRExitDescriptor
have a long list of booleans that correspond to different OSR exit types.
It now just has an enum property called ExceptionType that tells us
what type of exception handler the OSR exit is. Then, we can just ask
interesting questions about the OSR exit and get answers based on its
ExceptionType property.

* ftl/FTLCompile.cpp:
(JSC::FTL::mmAllocateDataSection):
* ftl/FTLExceptionHandlerManager.cpp:
(JSC::FTL::ExceptionHandlerManager::addNewExit):
(JSC::FTL::ExceptionHandlerManager::callOperationExceptionTarget):
(JSC::FTL::ExceptionHandlerManager::lazySlowPathExceptionTarget):
(JSC::FTL::ExceptionHandlerManager::getByIdOSRExit):
(JSC::FTL::ExceptionHandlerManager::subOSRExit):
(JSC::FTL::ExceptionHandlerManager::getCallOSRExitCommon):
(JSC::FTL::ExceptionHandlerManager::getOrPutByIdCallOperationExceptionTarget): Deleted.
* ftl/FTLExceptionHandlerManager.h:
* ftl/FTLExitThunkGenerator.cpp:
(JSC::FTL::ExitThunkGenerator::emitThunk):
* ftl/FTLLowerDFGToLLVM.cpp:
(JSC::FTL::DFG::LowerDFGToLLVM::lower):
(JSC::FTL::DFG::LowerDFGToLLVM::compileArithAddOrSub):
(JSC::FTL::DFG::LowerDFGToLLVM::compilePutById):
(JSC::FTL::DFG::LowerDFGToLLVM::compileCallOrConstruct):
(JSC::FTL::DFG::LowerDFGToLLVM::compileCallOrConstructVarargs):
(JSC::FTL::DFG::LowerDFGToLLVM::compileInvalidationPoint):
(JSC::FTL::DFG::LowerDFGToLLVM::getById):
(JSC::FTL::DFG::LowerDFGToLLVM::lazySlowPath):
(JSC::FTL::DFG::LowerDFGToLLVM::callCheck):
(JSC::FTL::DFG::LowerDFGToLLVM::appendOSRExitArgumentsForPatchpointIfWillCatchException):
(JSC::FTL::DFG::LowerDFGToLLVM::emitBranchToOSRExitIfWillCatchException):
(JSC::FTL::DFG::LowerDFGToLLVM::lowBlock):
(JSC::FTL::DFG::LowerDFGToLLVM::appendOSRExitDescriptor):
(JSC::FTL::DFG::LowerDFGToLLVM::appendOSRExit):
(JSC::FTL::DFG::LowerDFGToLLVM::buildExitArguments):
* ftl/FTLOSRExit.cpp:
(JSC::FTL::OSRExitDescriptor::OSRExitDescriptor):
(JSC::FTL::OSRExitDescriptor::willArriveAtExitFromIndirectExceptionCheck):
(JSC::FTL::OSRExitDescriptor::mightArriveAtOSRExitFromGenericUnwind):
(JSC::FTL::OSRExitDescriptor::mightArriveAtOSRExitFromCallOperation):
(JSC::FTL::OSRExitDescriptor::needsRegisterRecoveryOnGenericUnwindOSRExitPath):
(JSC::FTL::OSRExitDescriptor::isExceptionHandler):
(JSC::FTL::OSRExitDescriptor::validateReferences):
(JSC::FTL::OSRExit::OSRExit):
(JSC::FTL::OSRExit::codeLocationForRepatch):
(JSC::FTL::OSRExit::gatherRegistersToSpillForCallIfException):
(JSC::FTL::OSRExit::spillRegistersToSpillSlot):
(JSC::FTL::OSRExit::recoverRegistersFromSpillSlot):
* ftl/FTLOSRExit.h:
* ftl/FTLOSRExitCompilationInfo.h:
(JSC::FTL::OSRExitCompilationInfo::OSRExitCompilationInfo):
* ftl/FTLOSRExitCompiler.cpp:
(JSC::FTL::compileFTLOSRExit):
* tests/stress/ftl-try-catch-arith-sub-exception.js: Added.
(assert):
(let.o.valueOf):
(baz):
(foo):
(bar):</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceJavaScriptCoreChangeLog">trunk/Source/JavaScriptCore/ChangeLog</a></li>
<li><a href="#trunkSourceJavaScriptCoreftlFTLCompilecpp">trunk/Source/JavaScriptCore/ftl/FTLCompile.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreftlFTLExceptionHandlerManagercpp">trunk/Source/JavaScriptCore/ftl/FTLExceptionHandlerManager.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreftlFTLExceptionHandlerManagerh">trunk/Source/JavaScriptCore/ftl/FTLExceptionHandlerManager.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreftlFTLExitThunkGeneratorcpp">trunk/Source/JavaScriptCore/ftl/FTLExitThunkGenerator.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreftlFTLLowerDFGToLLVMcpp">trunk/Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreftlFTLOSRExitcpp">trunk/Source/JavaScriptCore/ftl/FTLOSRExit.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreftlFTLOSRExith">trunk/Source/JavaScriptCore/ftl/FTLOSRExit.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreftlFTLOSRExitCompilationInfoh">trunk/Source/JavaScriptCore/ftl/FTLOSRExitCompilationInfo.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreftlFTLOSRExitCompilercpp">trunk/Source/JavaScriptCore/ftl/FTLOSRExitCompiler.cpp</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkSourceJavaScriptCoretestsstressftltrycatcharithsubexceptionjs">trunk/Source/JavaScriptCore/tests/stress/ftl-try-catch-arith-sub-exception.js</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceJavaScriptCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ChangeLog (192438 => 192439)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ChangeLog        2015-11-13 21:29:54 UTC (rev 192438)
+++ trunk/Source/JavaScriptCore/ChangeLog        2015-11-13 21:36:18 UTC (rev 192439)
</span><span class="lines">@@ -1,3 +1,78 @@
</span><ins>+2015-11-13  Saam barati  &lt;sbarati@apple.com&gt;
+
+        sub IC does not properly handle exception handling now that try/catch is compiled in the FTL
+        https://bugs.webkit.org/show_bug.cgi?id=151080
+
+        Reviewed by Geoffrey Garen.
+
+        This patch implements proper exception handling for ArithSubs with binaryUseKind as
+        UntypedUse inside try/catch.  We implement this in a very similar way as we implement 
+        exception handling from GetById/PutById/LazySlowPaths callOperation calls. We do the needed 
+        spilling if the result is the same register as the left/right register because in the event
+        of an exception, we don't want to do value recovery on the garbage result, we
+        want to do it on the original lhs/rhs of the operation.
+
+        This patch also does some refactoring. No longer does OSRExitDescriptor
+        have a long list of booleans that correspond to different OSR exit types.
+        It now just has an enum property called ExceptionType that tells us
+        what type of exception handler the OSR exit is. Then, we can just ask
+        interesting questions about the OSR exit and get answers based on its
+        ExceptionType property.
+
+        * ftl/FTLCompile.cpp:
+        (JSC::FTL::mmAllocateDataSection):
+        * ftl/FTLExceptionHandlerManager.cpp:
+        (JSC::FTL::ExceptionHandlerManager::addNewExit):
+        (JSC::FTL::ExceptionHandlerManager::callOperationExceptionTarget):
+        (JSC::FTL::ExceptionHandlerManager::lazySlowPathExceptionTarget):
+        (JSC::FTL::ExceptionHandlerManager::getByIdOSRExit):
+        (JSC::FTL::ExceptionHandlerManager::subOSRExit):
+        (JSC::FTL::ExceptionHandlerManager::getCallOSRExitCommon):
+        (JSC::FTL::ExceptionHandlerManager::getOrPutByIdCallOperationExceptionTarget): Deleted.
+        * ftl/FTLExceptionHandlerManager.h:
+        * ftl/FTLExitThunkGenerator.cpp:
+        (JSC::FTL::ExitThunkGenerator::emitThunk):
+        * ftl/FTLLowerDFGToLLVM.cpp:
+        (JSC::FTL::DFG::LowerDFGToLLVM::lower):
+        (JSC::FTL::DFG::LowerDFGToLLVM::compileArithAddOrSub):
+        (JSC::FTL::DFG::LowerDFGToLLVM::compilePutById):
+        (JSC::FTL::DFG::LowerDFGToLLVM::compileCallOrConstruct):
+        (JSC::FTL::DFG::LowerDFGToLLVM::compileCallOrConstructVarargs):
+        (JSC::FTL::DFG::LowerDFGToLLVM::compileInvalidationPoint):
+        (JSC::FTL::DFG::LowerDFGToLLVM::getById):
+        (JSC::FTL::DFG::LowerDFGToLLVM::lazySlowPath):
+        (JSC::FTL::DFG::LowerDFGToLLVM::callCheck):
+        (JSC::FTL::DFG::LowerDFGToLLVM::appendOSRExitArgumentsForPatchpointIfWillCatchException):
+        (JSC::FTL::DFG::LowerDFGToLLVM::emitBranchToOSRExitIfWillCatchException):
+        (JSC::FTL::DFG::LowerDFGToLLVM::lowBlock):
+        (JSC::FTL::DFG::LowerDFGToLLVM::appendOSRExitDescriptor):
+        (JSC::FTL::DFG::LowerDFGToLLVM::appendOSRExit):
+        (JSC::FTL::DFG::LowerDFGToLLVM::buildExitArguments):
+        * ftl/FTLOSRExit.cpp:
+        (JSC::FTL::OSRExitDescriptor::OSRExitDescriptor):
+        (JSC::FTL::OSRExitDescriptor::willArriveAtExitFromIndirectExceptionCheck):
+        (JSC::FTL::OSRExitDescriptor::mightArriveAtOSRExitFromGenericUnwind):
+        (JSC::FTL::OSRExitDescriptor::mightArriveAtOSRExitFromCallOperation):
+        (JSC::FTL::OSRExitDescriptor::needsRegisterRecoveryOnGenericUnwindOSRExitPath):
+        (JSC::FTL::OSRExitDescriptor::isExceptionHandler):
+        (JSC::FTL::OSRExitDescriptor::validateReferences):
+        (JSC::FTL::OSRExit::OSRExit):
+        (JSC::FTL::OSRExit::codeLocationForRepatch):
+        (JSC::FTL::OSRExit::gatherRegistersToSpillForCallIfException):
+        (JSC::FTL::OSRExit::spillRegistersToSpillSlot):
+        (JSC::FTL::OSRExit::recoverRegistersFromSpillSlot):
+        * ftl/FTLOSRExit.h:
+        * ftl/FTLOSRExitCompilationInfo.h:
+        (JSC::FTL::OSRExitCompilationInfo::OSRExitCompilationInfo):
+        * ftl/FTLOSRExitCompiler.cpp:
+        (JSC::FTL::compileFTLOSRExit):
+        * tests/stress/ftl-try-catch-arith-sub-exception.js: Added.
+        (assert):
+        (let.o.valueOf):
+        (baz):
+        (foo):
+        (bar):
+
</ins><span class="cx"> 2015-11-13  Caitlin Potter  &lt;caitpotter88@gmail.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Allow any LeftHandSideExpression as a valid AssignmentElement
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreftlFTLCompilecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ftl/FTLCompile.cpp (192438 => 192439)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ftl/FTLCompile.cpp        2015-11-13 21:29:54 UTC (rev 192438)
+++ trunk/Source/JavaScriptCore/ftl/FTLCompile.cpp        2015-11-13 21:36:18 UTC (rev 192439)
</span><span class="lines">@@ -639,21 +639,36 @@
</span><span class="cx">             }
</span><span class="cx"> 
</span><span class="cx">             OSRExit&amp; exit = state.jitCode-&gt;osrExit.last();
</span><del>-            if (exitDescriptor.m_willArriveAtOSRExitFromGenericUnwind || exitDescriptor.m_isExceptionFromLazySlowPath) {
</del><ins>+            if (exitDescriptor.willArriveAtExitFromIndirectExceptionCheck()) {
</ins><span class="cx">                 StackMaps::Record&amp; record = iter-&gt;value[j].record;
</span><span class="cx">                 RELEASE_ASSERT(exit.m_descriptor.m_semanticCodeOriginForCallFrameHeader.isSet());
</span><span class="cx">                 CallSiteIndex callSiteIndex = state.jitCode-&gt;common.addUniqueCallSiteIndex(exit.m_descriptor.m_semanticCodeOriginForCallFrameHeader);
</span><span class="cx">                 exit.m_exceptionHandlerCallSiteIndex = callSiteIndex;
</span><span class="cx">                 exceptionHandlerManager.addNewExit(iter-&gt;value[j].index, state.jitCode-&gt;osrExit.size() - 1);
</span><span class="cx"> 
</span><del>-                if (exitDescriptor.m_isExceptionFromJSCall)
</del><ins>+                // Subs and GetByIds have an interesting register preservation story,
+                // see comment below at GetById to read about it.
+                //
+                // We set the registers needing spillage here because they need to be set
+                // before we generate OSR exits so the exit knows to do the proper recovery.
+                if (exitDescriptor.m_exceptionType == ExceptionType::JSCall) {
+                    // Call patchpoints might have values we want to do value recovery
+                    // on inside volatile registers. We need to collect the volatile
+                    // registers we want to do value recovery on here because they must
+                    // be preserved to the stack before the call, that way the OSR exit
+                    // exception handler can recover them into the proper registers.
</ins><span class="cx">                     exit.gatherRegistersToSpillForCallIfException(stackmaps, record);
</span><del>-                if (exitDescriptor.m_isExceptionFromGetById) {
</del><ins>+                } else if (exitDescriptor.m_exceptionType == ExceptionType::GetById) {
</ins><span class="cx">                     GPRReg result = record.locations[0].directGPR();
</span><span class="cx">                     GPRReg base = record.locations[1].directGPR();
</span><del>-                    // This has an interesting story, see comment below describing it.
-                    if (result == base)
</del><ins>+                    if (base == result)
</ins><span class="cx">                         exit.registersToPreserveForCallThatMightThrow.set(base);
</span><ins>+                } else if (exitDescriptor.m_exceptionType == ExceptionType::SubGenerator) {
+                    GPRReg result = record.locations[0].directGPR();
+                    GPRReg left = record.locations[1].directGPR();
+                    GPRReg right = record.locations[2].directGPR();
+                    if (result == left || result == right)
+                        exit.registersToPreserveForCallThatMightThrow.set(result);
</ins><span class="cx">                 }
</span><span class="cx">             }
</span><span class="cx">         }
</span><span class="lines">@@ -684,7 +699,7 @@
</span><span class="cx">             info.m_thunkAddress = linkBuffer-&gt;locationOf(info.m_thunkLabel);
</span><span class="cx">             exit.m_patchableCodeOffset = linkBuffer-&gt;offsetOf(info.m_thunkJump);
</span><span class="cx"> 
</span><del>-            if (exit.m_descriptor.m_willArriveAtOSRExitFromGenericUnwind) {
</del><ins>+            if (exit.m_descriptor.mightArriveAtOSRExitFromGenericUnwind()) {
</ins><span class="cx">                 HandlerInfo newHandler = exit.m_descriptor.m_baselineExceptionHandler;
</span><span class="cx">                 newHandler.start = exit.m_exceptionHandlerCallSiteIndex.bits();
</span><span class="cx">                 newHandler.end = exit.m_exceptionHandlerCallSiteIndex.bits() + 1;
</span><span class="lines">@@ -717,7 +732,7 @@
</span><span class="cx"> 
</span><span class="cx">         Vector&lt;std::pair&lt;CCallHelpers::JumpList, CodeLocationLabel&gt;&gt; exceptionJumpsToLink;
</span><span class="cx">         auto addNewExceptionJumpIfNecessary = [&amp;] (uint32_t recordIndex) {
</span><del>-            CodeLocationLabel exceptionTarget = exceptionHandlerManager.getOrPutByIdCallOperationExceptionTarget(recordIndex);
</del><ins>+            CodeLocationLabel exceptionTarget = exceptionHandlerManager.callOperationExceptionTarget(recordIndex);
</ins><span class="cx">             if (!exceptionTarget)
</span><span class="cx">                 return false;
</span><span class="cx">             exceptionJumpsToLink.append(
</span><span class="lines">@@ -877,8 +892,15 @@
</span><span class="cx">                 GPRReg right = record.locations[2].directGPR();
</span><span class="cx"> 
</span><span class="cx">                 arithSub.m_slowPathStarts.append(slowPathJIT.label());
</span><ins>+                bool addedUniqueExceptionJump = addNewExceptionJumpIfNecessary(iter-&gt;value[i].index);
+                if (result == left || result == right) {
+                    // This situation has a really interesting register preservation story.
+                    // See comment above for GetByIds.
+                    if (OSRExit* exit = exceptionHandlerManager.subOSRExit(iter-&gt;value[i].index))
+                        exit-&gt;spillRegistersToSpillSlot(slowPathJIT, jsCallThatMightThrowSpillOffset);
+                }
</ins><span class="cx"> 
</span><del>-                callOperation(state, usedRegisters, slowPathJIT, codeOrigin, &amp;exceptionTarget,
</del><ins>+                callOperation(state, usedRegisters, slowPathJIT, codeOrigin, addedUniqueExceptionJump ? &amp;exceptionJumpsToLink.last().first : &amp;exceptionTarget,
</ins><span class="cx">                     operationValueSub, result, left, right).call();
</span><span class="cx"> 
</span><span class="cx">                 arithSub.m_slowPathDone.append(slowPathJIT.jump());
</span><span class="lines">@@ -1058,7 +1080,7 @@
</span><span class="cx">         OSRExit&amp; exit = jitCode-&gt;osrExit[exitIndex];
</span><span class="cx">         Vector&lt;const void*&gt; codeAddresses;
</span><span class="cx"> 
</span><del>-        if (exit.m_descriptor.m_willArriveAtOSRExitFromGenericUnwind || exit.m_descriptor.m_isExceptionFromLazySlowPath) // This is reached by a jump from genericUnwind or a jump from a lazy slow path.
</del><ins>+        if (exit.m_descriptor.willArriveAtExitFromIndirectExceptionCheck()) // This jump doesn't happen directly from a patchpoint/stackmap we compile. It happens indirectly through an exception check somewhere.
</ins><span class="cx">             continue;
</span><span class="cx">         
</span><span class="cx">         StackMaps::Record&amp; record = jitCode-&gt;stackmaps.records[exit.m_stackmapRecordIndex];
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreftlFTLExceptionHandlerManagercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ftl/FTLExceptionHandlerManager.cpp (192438 => 192439)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ftl/FTLExceptionHandlerManager.cpp        2015-11-13 21:29:54 UTC (rev 192438)
+++ trunk/Source/JavaScriptCore/ftl/FTLExceptionHandlerManager.cpp        2015-11-13 21:36:18 UTC (rev 192439)
</span><span class="lines">@@ -41,20 +41,20 @@
</span><span class="cx"> {
</span><span class="cx">     m_map.add(stackmapRecordIndex, osrExitIndex);
</span><span class="cx">     OSRExit&amp; exit = m_state.jitCode-&gt;osrExit[osrExitIndex];
</span><del>-    RELEASE_ASSERT(exit.m_descriptor.m_willArriveAtOSRExitFromGenericUnwind || exit.m_descriptor.m_isExceptionFromLazySlowPath);
</del><ins>+    RELEASE_ASSERT(exit.m_descriptor.willArriveAtExitFromIndirectExceptionCheck());
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-CodeLocationLabel ExceptionHandlerManager::getOrPutByIdCallOperationExceptionTarget(uint32_t stackmapRecordIndex)
</del><ins>+CodeLocationLabel ExceptionHandlerManager::callOperationExceptionTarget(uint32_t stackmapRecordIndex)
</ins><span class="cx"> {
</span><span class="cx">     auto findResult = m_map.find(stackmapRecordIndex);
</span><span class="cx">     if (findResult == m_map.end())
</span><span class="cx">         return CodeLocationLabel();
</span><span class="cx"> 
</span><span class="cx">     size_t osrExitIndex = findResult-&gt;value;
</span><del>-    RELEASE_ASSERT(m_state.jitCode-&gt;osrExit[osrExitIndex].m_descriptor.m_willArriveAtOSRExitFromGenericUnwind);
</del><ins>+    RELEASE_ASSERT(m_state.jitCode-&gt;osrExit[osrExitIndex].m_descriptor.mightArriveAtOSRExitFromCallOperation());
</ins><span class="cx">     OSRExitCompilationInfo&amp; info = m_state.finalizer-&gt;osrExit[osrExitIndex];
</span><del>-    RELEASE_ASSERT(info.m_getAndPutByIdCallOperationExceptionOSRExitEntrance.isSet());
-    return m_state.finalizer-&gt;exitThunksLinkBuffer-&gt;locationOf(info.m_getAndPutByIdCallOperationExceptionOSRExitEntrance);
</del><ins>+    RELEASE_ASSERT(info.m_callOperationExceptionOSRExitEntrance.isSet());
+    return m_state.finalizer-&gt;exitThunksLinkBuffer-&gt;locationOf(info.m_callOperationExceptionOSRExitEntrance);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> CodeLocationLabel ExceptionHandlerManager::lazySlowPathExceptionTarget(uint32_t stackmapRecordIndex)
</span><span class="lines">@@ -64,7 +64,7 @@
</span><span class="cx">         return CodeLocationLabel();
</span><span class="cx"> 
</span><span class="cx">     size_t osrExitIndex = findResult-&gt;value;
</span><del>-    RELEASE_ASSERT(m_state.jitCode-&gt;osrExit[osrExitIndex].m_descriptor.m_isExceptionFromLazySlowPath);
</del><ins>+    RELEASE_ASSERT(m_state.jitCode-&gt;osrExit[osrExitIndex].m_descriptor.m_exceptionType == ExceptionType::LazySlowPath);
</ins><span class="cx">     OSRExitCompilationInfo&amp; info = m_state.finalizer-&gt;osrExit[osrExitIndex];
</span><span class="cx">     RELEASE_ASSERT(info.m_thunkLabel.isSet());
</span><span class="cx">     return m_state.finalizer-&gt;exitThunksLinkBuffer-&gt;locationOf(info.m_thunkLabel);
</span><span class="lines">@@ -77,10 +77,21 @@
</span><span class="cx">         return nullptr;
</span><span class="cx">     size_t osrExitIndex = findResult-&gt;value;
</span><span class="cx">     OSRExit* exit = &amp;m_state.jitCode-&gt;osrExit[osrExitIndex];
</span><del>-    RELEASE_ASSERT(exit-&gt;m_descriptor.m_isExceptionFromGetById);
</del><ins>+    RELEASE_ASSERT(exit-&gt;m_descriptor.m_exceptionType == ExceptionType::GetById);
</ins><span class="cx">     return exit; 
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+OSRExit* ExceptionHandlerManager::subOSRExit(uint32_t stackmapRecordIndex)
+{
+    auto findResult = m_map.find(stackmapRecordIndex);
+    if (findResult == m_map.end())
+        return nullptr;
+    size_t osrExitIndex = findResult-&gt;value;
+    OSRExit* exit = &amp;m_state.jitCode-&gt;osrExit[osrExitIndex];
+    RELEASE_ASSERT(exit-&gt;m_descriptor.m_exceptionType == ExceptionType::SubGenerator);
+    return exit; 
+}
+
</ins><span class="cx"> OSRExit* ExceptionHandlerManager::getCallOSRExitCommon(uint32_t stackmapRecordIndex)
</span><span class="cx"> {
</span><span class="cx">     auto findResult = m_map.find(stackmapRecordIndex);
</span><span class="lines">@@ -88,7 +99,7 @@
</span><span class="cx">         return nullptr;
</span><span class="cx">     size_t osrExitIndex = findResult-&gt;value;
</span><span class="cx">     OSRExit* exit = &amp;m_state.jitCode-&gt;osrExit[osrExitIndex];
</span><del>-    RELEASE_ASSERT(exit-&gt;m_descriptor.m_isExceptionFromJSCall);
</del><ins>+    RELEASE_ASSERT(exit-&gt;m_descriptor.m_exceptionType == ExceptionType::JSCall);
</ins><span class="cx">     return exit; 
</span><span class="cx"> }
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreftlFTLExceptionHandlerManagerh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ftl/FTLExceptionHandlerManager.h (192438 => 192439)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ftl/FTLExceptionHandlerManager.h        2015-11-13 21:29:54 UTC (rev 192438)
+++ trunk/Source/JavaScriptCore/ftl/FTLExceptionHandlerManager.h        2015-11-13 21:36:18 UTC (rev 192439)
</span><span class="lines">@@ -57,10 +57,11 @@
</span><span class="cx"> 
</span><span class="cx">     // These functions only make sense to be called after we've generated the OSR
</span><span class="cx">     // exit thunks and allocated the OSR exit thunks' link buffer.
</span><del>-    CodeLocationLabel getOrPutByIdCallOperationExceptionTarget(uint32_t stackmapRecordIndex);
</del><ins>+    CodeLocationLabel callOperationExceptionTarget(uint32_t stackmapRecordIndex);
</ins><span class="cx">     CodeLocationLabel lazySlowPathExceptionTarget(uint32_t stackmapRecordIndex);
</span><span class="cx"> 
</span><span class="cx">     OSRExit* getByIdOSRExit(uint32_t stackmapRecordIndex);
</span><ins>+    OSRExit* subOSRExit(uint32_t stackmapRecordIndex);
</ins><span class="cx">     OSRExit* getCallOSRExit(uint32_t stackmapRecordIndex, const JSCall&amp;);
</span><span class="cx">     OSRExit* getCallOSRExit(uint32_t stackmapRecordIndex, const JSTailCall&amp;);
</span><span class="cx">     OSRExit* getCallOSRExit(uint32_t stackmapRecordIndex, const JSCallVarargs&amp;);
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreftlFTLExitThunkGeneratorcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ftl/FTLExitThunkGenerator.cpp (192438 => 192439)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ftl/FTLExitThunkGenerator.cpp        2015-11-13 21:29:54 UTC (rev 192438)
+++ trunk/Source/JavaScriptCore/ftl/FTLExitThunkGenerator.cpp        2015-11-13 21:36:18 UTC (rev 192439)
</span><span class="lines">@@ -53,23 +53,26 @@
</span><span class="cx">     
</span><span class="cx">     info.m_thunkLabel = label();
</span><span class="cx"> 
</span><del>-    if (exit.m_descriptor.m_willArriveAtOSRExitFromGenericUnwind) {
</del><ins>+    Jump jumpToPushIndexFromGenericUnwind;
+    if (exit.m_descriptor.mightArriveAtOSRExitFromGenericUnwind()) {
</ins><span class="cx">         restoreCalleeSavesFromVMCalleeSavesBuffer();
</span><span class="cx">         loadPtr(vm()-&gt;addressOfCallFrameForCatch(), framePointerRegister);
</span><span class="cx">         addPtr(TrustedImm32(- static_cast&lt;int64_t&gt;(m_state.jitCode-&gt;stackmaps.stackSizeForLocals())), 
</span><span class="cx">             framePointerRegister, stackPointerRegister);
</span><span class="cx"> 
</span><del>-        if (exit.m_descriptor.m_isExceptionFromJSCall)
</del><ins>+        if (exit.m_descriptor.needsRegisterRecoveryOnGenericUnwindOSRExitPath())
</ins><span class="cx">             exit.recoverRegistersFromSpillSlot(*this, osrExitFromGenericUnwindStackSpillSlot);
</span><span class="cx"> 
</span><del>-        Jump skipGetAndPutByIdSlowPathEntrance = jump();
</del><ins>+        jumpToPushIndexFromGenericUnwind = jump();
+    }
</ins><span class="cx"> 
</span><del>-        info.m_getAndPutByIdCallOperationExceptionOSRExitEntrance = label();
-        if (exit.m_descriptor.m_isExceptionFromGetById)
-            exit.recoverRegistersFromSpillSlot(*this, osrExitFromGenericUnwindStackSpillSlot);
-        
-        skipGetAndPutByIdSlowPathEntrance.link(this);
</del><ins>+    if (exit.m_descriptor.mightArriveAtOSRExitFromCallOperation()) {
+        info.m_callOperationExceptionOSRExitEntrance = label();
+        exit.recoverRegistersFromSpillSlot(*this, osrExitFromGenericUnwindStackSpillSlot);
</ins><span class="cx">     }
</span><ins>+    
+    if (exit.m_descriptor.mightArriveAtOSRExitFromGenericUnwind())
+        jumpToPushIndexFromGenericUnwind.link(this);
</ins><span class="cx"> 
</span><span class="cx">     pushToSaveImmediateWithoutTouchingRegisters(TrustedImm32(index));
</span><span class="cx">     info.m_thunkJump = patchableJump();
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreftlFTLLowerDFGToLLVMcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp (192438 => 192439)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp        2015-11-13 21:29:54 UTC (rev 192438)
+++ trunk/Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp        2015-11-13 21:36:18 UTC (rev 192439)
</span><span class="lines">@@ -208,17 +208,19 @@
</span><span class="cx">                             maxNumberOfCatchSpills = std::max(maxNumberOfCatchSpills, m_graph.localsLiveInBytecode(opCatchOrigin).bitCount());
</span><span class="cx">                         break;
</span><span class="cx">                     }
</span><ins>+                    case ArithSub:
</ins><span class="cx">                     case GetById:
</span><span class="cx">                     case GetByIdFlush: {
</span><del>-                        // We may have to flush one thing for GetByIds when the base and result
-                        // are assigned the same register. For a more comprehensive overview, look
-                        // at the comment in FTLCompile.cpp
</del><ins>+                        // We may have to flush one thing for GetByIds/ArithSubs when the base and result or the left/right and the result
+                        // are assigned the same register. For a more comprehensive overview, look at the comment in FTLCompile.cpp
+                        if (node-&gt;op() == ArithSub &amp;&amp; node-&gt;binaryUseKind() != UntypedUse)
+                            break; // We only compile patchpoints for ArithSub UntypedUse.
</ins><span class="cx">                         CodeOrigin opCatchOrigin;
</span><span class="cx">                         HandlerInfo* exceptionHandler;
</span><span class="cx">                         bool willCatchException = m_graph.willCatchExceptionInMachineFrame(node-&gt;origin.forExit, opCatchOrigin, exceptionHandler);
</span><span class="cx">                         if (willCatchException) {
</span><del>-                            static const size_t numberOfGetByIdSpills = 1;
-                            maxNumberOfCatchSpills = std::max(maxNumberOfCatchSpills, numberOfGetByIdSpills);
</del><ins>+                            static const size_t numberOfGetByIdOrSubSpills = 1;
+                            maxNumberOfCatchSpills = std::max(maxNumberOfCatchSpills, numberOfGetByIdOrSubSpills);
</ins><span class="cx">                         }
</span><span class="cx">                         break;
</span><span class="cx">                     }
</span><span class="lines">@@ -1543,10 +1545,17 @@
</span><span class="cx">             LValue right = lowJSValue(m_node-&gt;child2());
</span><span class="cx"> 
</span><span class="cx">             // Arguments: id, bytes, target, numArgs, args...
</span><del>-            LValue call = m_out.call(
-                m_out.patchpointInt64Intrinsic(),
-                m_out.constInt64(stackmapID), m_out.constInt32(sizeOfArithSub()),
-                constNull(m_out.ref8), m_out.constInt32(2), left, right);
</del><ins>+            StackmapArgumentList arguments;
+            arguments.append(m_out.constInt64(stackmapID));
+            arguments.append(m_out.constInt32(sizeOfArithSub()));
+            arguments.append(constNull(m_out.ref8));
+            arguments.append(m_out.constInt32(2));
+            arguments.append(left);
+            arguments.append(right);
+
+            appendOSRExitArgumentsForPatchpointIfWillCatchException(arguments, ExceptionType::SubGenerator, 3); // left, right, and result show up in the stackmap locations.
+
+            LValue call = m_out.call(m_out.patchpointInt64Intrinsic(), arguments);
</ins><span class="cx">             setInstructionCallingConvention(call, LLVMAnyRegCallConv);
</span><span class="cx"> 
</span><span class="cx">             m_ftlState.arithSubs.append(ArithSubDescriptor(stackmapID, m_node-&gt;origin.semantic,
</span><span class="lines">@@ -2365,7 +2374,7 @@
</span><span class="cx">         arguments.append(base); 
</span><span class="cx">         arguments.append(value);
</span><span class="cx"> 
</span><del>-        appendOSRExitArgumentsForPatchpointIfWillCatchException(arguments, LLVMAnyRegCallConv, 2); // 2 arguments show up in the stackmap locations: the base and the value.
</del><ins>+        appendOSRExitArgumentsForPatchpointIfWillCatchException(arguments, ExceptionType::PutById, 2); // 2 arguments show up in the stackmap locations: the base and the value.
</ins><span class="cx"> 
</span><span class="cx">         arguments.insert(0, m_out.constInt32(2)); 
</span><span class="cx">         arguments.insert(0, constNull(m_out.ref8)); 
</span><span class="lines">@@ -4589,7 +4598,7 @@
</span><span class="cx">         for (unsigned i = 0; i &lt; padding; ++i)
</span><span class="cx">             arguments.append(getUndef(m_out.int64));
</span><span class="cx"> 
</span><del>-        appendOSRExitArgumentsForPatchpointIfWillCatchException(arguments, LLVMWebKitJSCallConv, 0); // No call arguments show up in the stackmap locations.
</del><ins>+        appendOSRExitArgumentsForPatchpointIfWillCatchException(arguments, ExceptionType::JSCall, 0); // No call arguments show up in the stackmap locations.
</ins><span class="cx"> 
</span><span class="cx">         arguments.insert(0, m_out.constInt32(1 + alignedFrameSize - JSStack::CallerFrameAndPCSize));
</span><span class="cx">         arguments.insert(0, constNull(m_out.ref8));
</span><span class="lines">@@ -4672,7 +4681,7 @@
</span><span class="cx">         ASSERT(thisArg);
</span><span class="cx">         arguments.append(thisArg);
</span><span class="cx"> 
</span><del>-        appendOSRExitArgumentsForPatchpointIfWillCatchException(arguments, LLVMCCallConv, 0); // No call arguments show up in stackmap locations.
</del><ins>+        appendOSRExitArgumentsForPatchpointIfWillCatchException(arguments, ExceptionType::JSCall, 0); // No call arguments show up in stackmap locations.
</ins><span class="cx"> 
</span><span class="cx">         arguments.insert(0, m_out.constInt32(2 + !!jsArguments));
</span><span class="cx">         arguments.insert(0, constNull(m_out.ref8));
</span><span class="lines">@@ -5054,11 +5063,8 @@
</span><span class="cx"> 
</span><span class="cx">         DFG_ASSERT(m_graph, m_node, m_origin.exitOK);
</span><span class="cx">         
</span><del>-        m_ftlState.jitCode-&gt;osrExitDescriptors.append(OSRExitDescriptor(
-            UncountableInvalidation, DataFormatNone, MethodOfGettingAValueProfile(),
-            m_origin.forExit, m_origin.semantic,
-            availabilityMap().m_locals.numberOfArguments(),
-            availabilityMap().m_locals.numberOfLocals()));
</del><ins>+
+        appendOSRExitDescriptor(UncountableInvalidation, ExceptionType::None, noValue(), nullptr, m_origin);
</ins><span class="cx">         
</span><span class="cx">         OSRExitDescriptor&amp; exitDescriptor = m_ftlState.jitCode-&gt;osrExitDescriptors.last();
</span><span class="cx">         
</span><span class="lines">@@ -6241,7 +6247,7 @@
</span><span class="cx">         StackmapArgumentList arguments;
</span><span class="cx">         arguments.append(base);
</span><span class="cx"> 
</span><del>-        appendOSRExitArgumentsForPatchpointIfWillCatchException(arguments, LLVMAnyRegCallConv, 2, false, true); // 2 arguments show up in the stackmap locations: the result and the base.
</del><ins>+        appendOSRExitArgumentsForPatchpointIfWillCatchException(arguments, ExceptionType::GetById, 2); // 2 arguments show up in the stackmap locations: the result and the base.
</ins><span class="cx"> 
</span><span class="cx">         arguments.insert(0, m_out.constInt32(1)); 
</span><span class="cx">         arguments.insert(0, constNull(m_out.ref8));
</span><span class="lines">@@ -7596,7 +7602,7 @@
</span><span class="cx">         arguments.append(m_out.constInt32(userArguments.size()));
</span><span class="cx">         arguments.appendVector(userArguments);
</span><span class="cx"> 
</span><del>-        appendOSRExitArgumentsForPatchpointIfWillCatchException(arguments, LLVMAnyRegCallConv, userArguments.size() + 1, true); // All the arguments plus the result show up in the stackmap locations.
</del><ins>+        appendOSRExitArgumentsForPatchpointIfWillCatchException(arguments, ExceptionType::LazySlowPath, userArguments.size() + 1); // All the arguments plus the result show up in the stackmap locations.
</ins><span class="cx"> 
</span><span class="cx">         LValue call = m_out.call(m_out.patchpointInt64Intrinsic(), arguments);
</span><span class="cx">         setInstructionCallingConvention(call, LLVMAnyRegCallConv);
</span><span class="lines">@@ -8845,7 +8851,7 @@
</span><span class="cx">         m_out.appendTo(continuation);
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    void appendOSRExitArgumentsForPatchpointIfWillCatchException(StackmapArgumentList&amp; arguments, LCallConv callingConvention, unsigned offsetOfExitArguments, bool isLazySlowPath = false, bool isGetBydId = false)
</del><ins>+    void appendOSRExitArgumentsForPatchpointIfWillCatchException(StackmapArgumentList&amp; arguments, ExceptionType exceptionType, unsigned offsetOfExitArguments)
</ins><span class="cx">     {
</span><span class="cx">         CodeOrigin opCatchOrigin;
</span><span class="cx">         HandlerInfo* exceptionHandler;
</span><span class="lines">@@ -8853,27 +8859,15 @@
</span><span class="cx">         if (!willCatchException)
</span><span class="cx">             return;
</span><span class="cx"> 
</span><del>-        appendOSRExitDescriptor(Uncountable, noValue(), nullptr, m_origin.withForExitAndExitOK(opCatchOrigin, true));
</del><ins>+        appendOSRExitDescriptor(Uncountable, exceptionType, noValue(), nullptr, m_origin.withForExitAndExitOK(opCatchOrigin, true));
</ins><span class="cx">         OSRExitDescriptor&amp; exitDescriptor = m_ftlState.jitCode-&gt;osrExitDescriptors.last();
</span><span class="cx">         exitDescriptor.m_semanticCodeOriginForCallFrameHeader = codeOriginDescriptionOfCallSite();
</span><del>-        exitDescriptor.m_isExceptionHandler = true;
-        exitDescriptor.m_willArriveAtOSRExitFromGenericUnwind = !isLazySlowPath;
-        exitDescriptor.m_isExceptionFromLazySlowPath = isLazySlowPath;
-        exitDescriptor.m_isExceptionFromJSCall = callingConvention == LLVMWebKitJSCallConv || callingConvention == LLVMCCallConv;
-        exitDescriptor.m_isExceptionFromGetById = isGetBydId;
</del><span class="cx">         exitDescriptor.m_baselineExceptionHandler = *exceptionHandler;
</span><span class="cx">         exitDescriptor.m_stackmapID = m_stackmapIDs - 1;
</span><span class="cx"> 
</span><span class="cx">         StackmapArgumentList freshList;
</span><del>-        buildExitArguments(exitDescriptor, freshList, noValue(), exitDescriptor.m_codeOrigin);
</del><ins>+        buildExitArguments(exitDescriptor, freshList, noValue(), exitDescriptor.m_codeOrigin, offsetOfExitArguments);
</ins><span class="cx">         arguments.appendVector(freshList);
</span><del>-
-        if (offsetOfExitArguments) {
-            for (size_t i = 0; i &lt; exitDescriptor.m_values.size(); i++) {
-                if (exitDescriptor.m_values[i].hasIndexInStackmapLocations())
-                    exitDescriptor.m_values[i].adjustStackmapLocationsIndexByOffset(offsetOfExitArguments);
-            }
-        }
</del><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     bool emitBranchToOSRExitIfWillCatchException(LValue hadException)
</span><span class="lines">@@ -8893,10 +8887,10 @@
</span><span class="cx">         return m_blocks.get(block);
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    void appendOSRExitDescriptor(ExitKind kind, FormattedValue lowValue, Node* highValue, NodeOrigin origin)
</del><ins>+    void appendOSRExitDescriptor(ExitKind kind, ExceptionType exceptionType, FormattedValue lowValue, Node* highValue, NodeOrigin origin)
</ins><span class="cx">     {
</span><span class="cx">         m_ftlState.jitCode-&gt;osrExitDescriptors.append(OSRExitDescriptor(
</span><del>-            kind, lowValue.format(), m_graph.methodOfGettingAValueProfileFor(highValue),
</del><ins>+            kind, exceptionType, lowValue.format(), m_graph.methodOfGettingAValueProfileFor(highValue),
</ins><span class="cx">             origin.forExit, origin.semantic,
</span><span class="cx">             availabilityMap().m_locals.numberOfArguments(),
</span><span class="cx">             availabilityMap().m_locals.numberOfLocals()));
</span><span class="lines">@@ -8936,9 +8930,8 @@
</span><span class="cx">         if (failCondition == m_out.booleanFalse)
</span><span class="cx">             return;
</span><span class="cx"> 
</span><del>-        appendOSRExitDescriptor(kind, lowValue, highValue, origin);
</del><ins>+        appendOSRExitDescriptor(kind, isExceptionHandler ? ExceptionType::CCallException : ExceptionType::None, lowValue, highValue, origin);
</ins><span class="cx">         OSRExitDescriptor&amp; exitDescriptor = m_ftlState.jitCode-&gt;osrExitDescriptors.last();
</span><del>-        exitDescriptor.m_isExceptionHandler = isExceptionHandler;
</del><span class="cx"> 
</span><span class="cx">         if (failCondition == m_out.booleanTrue) {
</span><span class="cx">             emitOSRExitCall(exitDescriptor, lowValue);
</span><span class="lines">@@ -8975,7 +8968,7 @@
</span><span class="cx">     
</span><span class="cx">     void buildExitArguments(
</span><span class="cx">         OSRExitDescriptor&amp; exitDescriptor, StackmapArgumentList&amp; arguments, FormattedValue lowValue,
</span><del>-        CodeOrigin codeOrigin)
</del><ins>+        CodeOrigin codeOrigin, unsigned offsetOfExitArgumentsInStackmapLocations = 0)
</ins><span class="cx">     {
</span><span class="cx">         if (!!lowValue)
</span><span class="cx">             arguments.append(lowValue.value());
</span><span class="lines">@@ -9010,16 +9003,21 @@
</span><span class="cx">                     m_graph, m_node,
</span><span class="cx">                     (!(availability.isDead() &amp;&amp; m_graph.isLiveInBytecode(VirtualRegister(operand), codeOrigin))) || m_graph.m_plan.mode == FTLForOSREntryMode);
</span><span class="cx">             }
</span><del>-            
-            exitDescriptor.m_values[i] = exitValueForAvailability(arguments, map, availability);
</del><ins>+            ExitValue exitValue = exitValueForAvailability(arguments, map, availability);
+            if (exitValue.hasIndexInStackmapLocations())
+                exitValue.adjustStackmapLocationsIndexByOffset(offsetOfExitArgumentsInStackmapLocations);
+            exitDescriptor.m_values[i] = exitValue;
</ins><span class="cx">         }
</span><span class="cx">         
</span><span class="cx">         for (auto heapPair : availabilityMap.m_heap) {
</span><span class="cx">             Node* node = heapPair.key.base();
</span><span class="cx">             ExitTimeObjectMaterialization* materialization = map.get(node);
</span><ins>+            ExitValue exitValue = exitValueForAvailability(arguments, map, heapPair.value);
+            if (exitValue.hasIndexInStackmapLocations())
+                exitValue.adjustStackmapLocationsIndexByOffset(offsetOfExitArgumentsInStackmapLocations);
</ins><span class="cx">             materialization-&gt;add(
</span><span class="cx">                 heapPair.key.descriptor(),
</span><del>-                exitValueForAvailability(arguments, map, heapPair.value));
</del><ins>+                exitValue);
</ins><span class="cx">         }
</span><span class="cx">         
</span><span class="cx">         if (verboseCompilationEnabled()) {
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreftlFTLOSRExitcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ftl/FTLOSRExit.cpp (192438 => 192439)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ftl/FTLOSRExit.cpp        2015-11-13 21:29:54 UTC (rev 192438)
+++ trunk/Source/JavaScriptCore/ftl/FTLOSRExit.cpp        2015-11-13 21:36:18 UTC (rev 192439)
</span><span class="lines">@@ -41,25 +41,82 @@
</span><span class="cx"> using namespace DFG;
</span><span class="cx"> 
</span><span class="cx"> OSRExitDescriptor::OSRExitDescriptor(
</span><del>-    ExitKind exitKind, DataFormat profileDataFormat,
</del><ins>+    ExitKind exitKind, ExceptionType exceptionType, DataFormat profileDataFormat,
</ins><span class="cx">     MethodOfGettingAValueProfile valueProfile, CodeOrigin codeOrigin,
</span><span class="cx">     CodeOrigin originForProfile, unsigned numberOfArguments,
</span><span class="cx">     unsigned numberOfLocals)
</span><span class="cx">     : m_kind(exitKind)
</span><ins>+    , m_exceptionType(exceptionType)
</ins><span class="cx">     , m_codeOrigin(codeOrigin)
</span><span class="cx">     , m_codeOriginForExitProfile(originForProfile)
</span><span class="cx">     , m_profileDataFormat(profileDataFormat)
</span><span class="cx">     , m_valueProfile(valueProfile)
</span><span class="cx">     , m_values(numberOfArguments, numberOfLocals)
</span><span class="cx">     , m_isInvalidationPoint(false)
</span><del>-    , m_isExceptionHandler(false)
-    , m_willArriveAtOSRExitFromGenericUnwind(false)
-    , m_isExceptionFromJSCall(false)
-    , m_isExceptionFromGetById(false)
-    , m_isExceptionFromLazySlowPath(false)
</del><span class="cx"> {
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+bool OSRExitDescriptor::willArriveAtExitFromIndirectExceptionCheck() const
+{
+    switch (m_exceptionType) {
+    case ExceptionType::JSCall:
+    case ExceptionType::GetById:
+    case ExceptionType::PutById:
+    case ExceptionType::LazySlowPath:
+    case ExceptionType::SubGenerator:
+        return true;
+    default:
+        return false;
+    }
+    RELEASE_ASSERT_NOT_REACHED();
+}
+
+bool OSRExitDescriptor::mightArriveAtOSRExitFromGenericUnwind() const
+{
+    switch (m_exceptionType) {
+    case ExceptionType::JSCall:
+    case ExceptionType::GetById:
+    case ExceptionType::PutById:
+        return true;
+    default:
+        return false;
+    }
+    RELEASE_ASSERT_NOT_REACHED();
+}
+
+bool OSRExitDescriptor::mightArriveAtOSRExitFromCallOperation() const
+{
+    switch (m_exceptionType) {
+    case ExceptionType::GetById:
+    case ExceptionType::PutById:
+    case ExceptionType::SubGenerator:
+        return true;
+    default:
+        return false;
+    }
+    RELEASE_ASSERT_NOT_REACHED();
+}
+
+bool OSRExitDescriptor::needsRegisterRecoveryOnGenericUnwindOSRExitPath() const
+{
+    // Calls/PutByIds/GetByIds all have a generic unwind osr exit paths.
+    // But, GetById and PutById ICs will do register recovery themselves
+    // because they're responsible for spilling necessary registers, so
+    // they also must recover registers themselves.
+    // Calls don't work this way. We compile Calls as patchpoints in LLVM.
+    // A call patchpoint might pass us volatile registers for locations
+    // we will do value recovery on. Therefore, before we make the call,
+    // we must spill these registers. Otherwise, the call will clobber them.
+    // Therefore, the corresponding OSR exit for the call will need to
+    // recover the spilled registers.
+    return m_exceptionType == ExceptionType::JSCall;
+}
+
+bool OSRExitDescriptor::isExceptionHandler() const
+{
+    return m_exceptionType != ExceptionType::None;
+}
+
</ins><span class="cx"> void OSRExitDescriptor::validateReferences(const TrackedReferences&amp; trackedReferences)
</span><span class="cx"> {
</span><span class="cx">     for (unsigned i = m_values.size(); i--;)
</span><span class="lines">@@ -75,7 +132,7 @@
</span><span class="cx">     , m_descriptor(descriptor)
</span><span class="cx">     , m_stackmapRecordIndex(stackmapRecordIndex)
</span><span class="cx"> {
</span><del>-    m_isExceptionHandler = descriptor.m_isExceptionHandler;
</del><ins>+    m_isExceptionHandler = descriptor.isExceptionHandler();
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> CodeLocationJump OSRExit::codeLocationForRepatch(CodeBlock* ftlCodeBlock) const
</span><span class="lines">@@ -88,7 +145,7 @@
</span><span class="cx"> 
</span><span class="cx"> void OSRExit::gatherRegistersToSpillForCallIfException(StackMaps&amp; stackmaps, StackMaps::Record&amp; record)
</span><span class="cx"> {
</span><del>-    RELEASE_ASSERT(m_descriptor.m_isExceptionFromJSCall);
</del><ins>+    RELEASE_ASSERT(m_descriptor.m_exceptionType == ExceptionType::JSCall);
</ins><span class="cx"> 
</span><span class="cx">     RegisterSet volatileRegisters = RegisterSet::volatileRegistersForJSCall();
</span><span class="cx"> 
</span><span class="lines">@@ -122,7 +179,7 @@
</span><span class="cx"> 
</span><span class="cx"> void OSRExit::spillRegistersToSpillSlot(CCallHelpers&amp; jit, int32_t stackSpillSlot)
</span><span class="cx"> {
</span><del>-    RELEASE_ASSERT(m_descriptor.m_isExceptionFromJSCall || m_descriptor.m_isExceptionFromGetById);
</del><ins>+    RELEASE_ASSERT(m_descriptor.mightArriveAtOSRExitFromGenericUnwind() || m_descriptor.mightArriveAtOSRExitFromCallOperation());
</ins><span class="cx">     unsigned count = 0;
</span><span class="cx">     for (GPRReg reg = MacroAssembler::firstRegister(); reg &lt;= MacroAssembler::lastRegister(); reg = MacroAssembler::nextRegister(reg)) {
</span><span class="cx">         if (registersToPreserveForCallThatMightThrow.get(reg)) {
</span><span class="lines">@@ -140,7 +197,7 @@
</span><span class="cx"> 
</span><span class="cx"> void OSRExit::recoverRegistersFromSpillSlot(CCallHelpers&amp; jit, int32_t stackSpillSlot)
</span><span class="cx"> {
</span><del>-    RELEASE_ASSERT(m_descriptor.m_isExceptionFromJSCall || m_descriptor.m_isExceptionFromGetById);
</del><ins>+    RELEASE_ASSERT(m_descriptor.mightArriveAtOSRExitFromGenericUnwind() || m_descriptor.mightArriveAtOSRExitFromCallOperation());
</ins><span class="cx">     unsigned count = 0;
</span><span class="cx">     for (GPRReg reg = MacroAssembler::firstRegister(); reg &lt;= MacroAssembler::lastRegister(); reg = MacroAssembler::nextRegister(reg)) {
</span><span class="cx">         if (registersToPreserveForCallThatMightThrow.get(reg)) {
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreftlFTLOSRExith"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ftl/FTLOSRExit.h (192438 => 192439)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ftl/FTLOSRExit.h        2015-11-13 21:29:54 UTC (rev 192438)
+++ trunk/Source/JavaScriptCore/ftl/FTLOSRExit.h        2015-11-13 21:36:18 UTC (rev 192439)
</span><span class="lines">@@ -136,13 +136,30 @@
</span><span class="cx"> //   intrinsics (or meta-data, or something) to inform the backend that it's safe to
</span><span class="cx"> //   make the predicate passed to 'exitIf()' more truthy.
</span><span class="cx"> 
</span><ins>+enum class ExceptionType {
+    None,
+    CCallException,
+    JSCall,
+    GetById,
+    PutById,
+    LazySlowPath,
+    SubGenerator
+};
+
</ins><span class="cx"> struct OSRExitDescriptor {
</span><span class="cx">     OSRExitDescriptor(
</span><del>-        ExitKind, DataFormat profileDataFormat, MethodOfGettingAValueProfile,
</del><ins>+        ExitKind, ExceptionType, DataFormat profileDataFormat, MethodOfGettingAValueProfile,
</ins><span class="cx">         CodeOrigin, CodeOrigin originForProfile,
</span><span class="cx">         unsigned numberOfArguments, unsigned numberOfLocals);
</span><span class="cx"> 
</span><ins>+    bool willArriveAtExitFromIndirectExceptionCheck() const;
+    bool mightArriveAtOSRExitFromGenericUnwind() const;
+    bool mightArriveAtOSRExitFromCallOperation() const;
+    bool needsRegisterRecoveryOnGenericUnwindOSRExitPath() const;
+    bool isExceptionHandler() const;
+
</ins><span class="cx">     ExitKind m_kind;
</span><ins>+    ExceptionType m_exceptionType;
</ins><span class="cx">     CodeOrigin m_codeOrigin;
</span><span class="cx">     CodeOrigin m_codeOriginForExitProfile;
</span><span class="cx">     CodeOrigin m_semanticCodeOriginForCallFrameHeader;
</span><span class="lines">@@ -161,11 +178,6 @@
</span><span class="cx">     uint32_t m_stackmapID;
</span><span class="cx">     HandlerInfo m_baselineExceptionHandler;
</span><span class="cx">     bool m_isInvalidationPoint : 1;
</span><del>-    bool m_isExceptionHandler : 1;
-    bool m_willArriveAtOSRExitFromGenericUnwind : 1;
-    bool m_isExceptionFromJSCall : 1;
-    bool m_isExceptionFromGetById : 1;
-    bool m_isExceptionFromLazySlowPath : 1;
</del><span class="cx">     
</span><span class="cx">     void validateReferences(const TrackedReferences&amp;);
</span><span class="cx"> };
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreftlFTLOSRExitCompilationInfoh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ftl/FTLOSRExitCompilationInfo.h (192438 => 192439)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ftl/FTLOSRExitCompilationInfo.h        2015-11-13 21:29:54 UTC (rev 192438)
+++ trunk/Source/JavaScriptCore/ftl/FTLOSRExitCompilationInfo.h        2015-11-13 21:36:18 UTC (rev 192439)
</span><span class="lines">@@ -39,7 +39,7 @@
</span><span class="cx">     }
</span><span class="cx">     
</span><span class="cx">     MacroAssembler::Label m_thunkLabel;
</span><del>-    MacroAssembler::Label m_getAndPutByIdCallOperationExceptionOSRExitEntrance;
</del><ins>+    MacroAssembler::Label m_callOperationExceptionOSRExitEntrance;
</ins><span class="cx">     MacroAssembler::PatchableJump m_thunkJump;
</span><span class="cx">     CodeLocationLabel m_thunkAddress;
</span><span class="cx"> };
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreftlFTLOSRExitCompilercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ftl/FTLOSRExitCompiler.cpp (192438 => 192439)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ftl/FTLOSRExitCompiler.cpp        2015-11-13 21:29:54 UTC (rev 192438)
+++ trunk/Source/JavaScriptCore/ftl/FTLOSRExitCompiler.cpp        2015-11-13 21:36:18 UTC (rev 192439)
</span><span class="lines">@@ -547,8 +547,8 @@
</span><span class="cx">         dataLog(&quot;    Exit stackmap ID: &quot;, exit.m_descriptor.m_stackmapID, &quot;\n&quot;);
</span><span class="cx">         dataLog(&quot;    Current call site index: &quot;, exec-&gt;callSiteIndex().bits(), &quot;\n&quot;);
</span><span class="cx">         dataLog(&quot;    Exit is exception handler: &quot;, exit.m_isExceptionHandler,
</span><del>-            &quot; will arrive at exit from genericUnwind(): &quot;, exit.m_descriptor.m_willArriveAtOSRExitFromGenericUnwind, 
-            &quot; will arrive at exit from lazy slow path: &quot;, exit.m_descriptor.m_isExceptionFromLazySlowPath, &quot;\n&quot;);
</del><ins>+            &quot; might arrive at exit from genericUnwind(): &quot;, exit.m_descriptor.mightArriveAtOSRExitFromGenericUnwind(), 
+            &quot; will arrive at exit from lazy slow path: &quot;, exit.m_descriptor.m_exceptionType == ExceptionType::LazySlowPath, &quot;\n&quot;);
</ins><span class="cx">         dataLog(&quot;    Exit values: &quot;, exit.m_descriptor.m_values, &quot;\n&quot;);
</span><span class="cx">         if (!exit.m_descriptor.m_materializations.isEmpty()) {
</span><span class="cx">             dataLog(&quot;    Materializations:\n&quot;);
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoretestsstressftltrycatcharithsubexceptionjs"></a>
<div class="addfile"><h4>Added: trunk/Source/JavaScriptCore/tests/stress/ftl-try-catch-arith-sub-exception.js (0 => 192439)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/tests/stress/ftl-try-catch-arith-sub-exception.js                                (rev 0)
+++ trunk/Source/JavaScriptCore/tests/stress/ftl-try-catch-arith-sub-exception.js        2015-11-13 21:36:18 UTC (rev 192439)
</span><span class="lines">@@ -0,0 +1,59 @@
</span><ins>+function assert(b) {
+    if (!b)
+        throw new Error(&quot;uh oh&quot;);
+}
+
+let flag = false;
+let o = {
+    valueOf() {
+        if (flag)
+            throw new Error(&quot;by by&quot;);
+        return 13.5;
+    }
+};
+noInline(o.valueOf);
+
+function baz() { return 1.5; }
+noInline(baz);
+
+function foo(x, o) {
+    let r = baz();
+    try {
+        r = x - o - r;
+    } catch(e) { }
+    return r;
+}
+noInline(foo);
+
+let x = 20.5;
+for (let i = 0; i &lt; 10000; i++) {
+    assert(foo(x, o) === 5.5);
+}
+flag = true;
+assert(foo(x, o) === 1.5);
+
+
+function bar(x, o) {
+    let caughtException = false;
+    var r = null;
+    try {
+        // This tests aliasing of left/right with result register in a SubGenerator
+        // and ensures that the sub will spill the register properly and that we value
+        // recover properly.
+        r = x - o;
+    } catch(e) {
+        caughtException = true;
+        assert(r === null);
+    }
+    if (!caughtException)
+        assert(r === 7);
+    return caughtException;
+} 
+noInline(bar);
+
+flag = false;
+for (let i = 0; i &lt; 10000; i++) {
+    assert(bar(x, o) === false);
+}
+flag = true;
+assert(bar(x, o) === true);
</ins></span></pre>
</div>
</div>

</body>
</html>