<!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>[188979] trunk/Source</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/188979">188979</a></dd>
<dt>Author</dt> <dd>fpizlo@apple.com</dd>
<dt>Date</dt> <dd>2015-08-26 12:24:41 -0700 (Wed, 26 Aug 2015)</dd>
</dl>
<h3>Log Message</h3>
<pre>Node::origin should be able to tell you if it's OK to exit
https://bugs.webkit.org/show_bug.cgi?id=145204
Reviewed by Geoffrey Garen.
Source/JavaScriptCore:
This is a major change to DFG IR, that makes it easier to reason about where nodes with
speculations can be soundly hoisted.
A program in DFG IR is a sequence of operations that compute the values of SSA variables,
perform effects on the heap or stack, and perform updates to the OSR exit state. Because
effects and OSR exit updates are interleaved, there are points in execution where exiting
simply won't work. For example, we may have some bytecode operation:
[ 24] op_foo loc42 // does something, and puts a value in loc42.
that gets compiled down to a sequence of DFG IR nodes like:
a: Foo(W:Heap, R:World, bc#24) // writes heap, reads world - i.e. an observable effect.
b: MovHint(@a, loc42, bc#24)
c: SetLocal(Check:Int32:@a, loc42, bc#24, exit: bc#26)
Note that we can OSR exit at @a because we haven't yet performed any effects for bc#24 yet and
we have performed all effects for prior bytecode operations. That's what the origin.forExit
being set to "bc#24" guarantees. So, an OSR exit at @a would transfer execution to bc#24 and
this would not be observable. But at @b, if we try to exit to bc#24 as indicated by forExit, we
would end up causing the side effect of bc#24 to execute a second time. This would be
observable, so we cannot do it. And we cannot exit to the next instruction - bc#26 - either,
because @b is responsible for updating the OSR state to indicate that the result of @a should
be put into loc42. It's not until we get to @c that we can exit again.
This is a confusing, but useful, property of DFG IR. It's useful because it allows us to use IR
to spell out how we would have affected the bytecode state, and we use this to implement hard
things like object allocation elimination, where we use IR instructions to indicate what object
allocation and mutation operations we would have performed, and which bytecode variables would
have pointed to those objects. So long as IR allows us to describe how OSR exit state is
updated, there will be points in execution where that state is invalid - especially if the IR
to update exit state is separate from the IR to perform actual effects.
But this property is super confusing! It's difficult to explain that somehow magically, @b is a
bad place to put OSR exits, and that magically we will only have OSR exits at @a. Of course, it
all kind of makes sense - we insert OSR exit checks in phases that *know* where it's safe to
exit - but it's just too opaque. This also gets in the way of more sophisticated
transformations. For example, LICM barely works - it magically knows that loop pre-headers are
good places to exit from, but it has no way of determining if that is actually true. It would
be odd to introduce a restriction that anytime some block qualifies as a pre-header according
to our loop calculator, it must end with a terminal at which it is OK to exit. So, our choices
are to either leave LICM in a magical state and exercise extreme caution when introducing new
optimizations that hoist checks, or to do something to make the "can I exit here" property more
explicit in IR.
We have already, in a separate change, added a NodeOrigin::exitOK property, though it didn't do
anything yet. This change puts exitOK to work, and makes it an integral part of IR. The key
intuition behind this change is that if we know which nodes clobber exit state - i.e. after the
node, it's no longer possible to OSR exit until the exit state is fixed up - then we can figure
out where it's fine to exit. This change mostly adopts the already implicit rule that it's
always safe to exit right at the boundary of exit origins (in between two nodes where
origin.forExit differs), and adds a new node, called ExitOK, which is a kind of declaration
that exit state is good again. When making this change, I struggled with the question of
whether to make origin.exitOK be explicit, or something that we can compute with an analysis.
Of course if we are armed with a clobbersExitState(Node*) function, we can find the places
where it's fine to exit. But this kind of computation could get quite sophisticated if the
nodes belonging to an exit origin are lowered to a control-flow construct. It would also be
harder to see what the original intent was, if we found an error: is the bug that we shouldn't
be clobbering exit state, or that we shouldn't be exiting? This change opts to make exitOK be
an explicit property of IR, so that DFG IR validation will reject any program where exitOK is
true after a node that clobbersExitState(), or if exitOK is true after a node has exitOK set to
false - unless the latter node has a different exit origin or is an ExitOK node. It will also
reject any program where a node mayExit() with !exitOK.
It turns out that this revealed a lot of sloppiness and what almost looked like an outright
bug: the callee property of an inline closure call frame was being set up "as if" by the
callee's op_enter. If we did hoist a check per the old rule - to the boundary of exit origins -
then we would crash because the callee is unknown. It also revealed that LICM could *almost*
get hosed by having a pre-header where there are effects before the jump. I wasn't able to
construct a test case that would crash trunk, but I also couldn't quite prove why such a
program couldn't be constructed. I did fix the issue in loop pre-header creation, and the
validater does catch the issue because of its exitOK assertions.
This doesn't yet add any other safeguards to LICM - that phase still expects that pre-headers
are in place and that they were created in such a way that their terminal origins have exitOK.
It also still keeps the old way of saying "not OK to exit" - having a clear NodeOrigin. In a
later patch I'll remove that and use !exitOK everywhere. Note that I did consider using clear
NodeOrigins to signify that it's not OK to exit, but that would make DFGForAllKills a lot more
expensive - it would have to sometimes search to find nearby forExit origins if the current
node doesn't have it set - and that's a critical phase for DFG compilation performance.
Requiring that forExit is usually set to *something* and that properly shadows the original
bytecode is cheap and easy, so it seemed like a good trade-off.
This change has no performance effect. Its only effect is that it makes the compiler easier to
understand by turning a previously magical concept into an explicit one.
* CMakeLists.txt:
* JavaScriptCore.vcxproj/JavaScriptCore.vcxproj:
* JavaScriptCore.xcodeproj/project.pbxproj:
* dfg/DFGAbstractHeap.h:
* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGArgumentsEliminationPhase.cpp:
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::setDirect):
(JSC::DFG::ByteCodeParser::currentNodeOrigin):
(JSC::DFG::ByteCodeParser::branchData):
(JSC::DFG::ByteCodeParser::addToGraph):
(JSC::DFG::ByteCodeParser::handleCall):
(JSC::DFG::ByteCodeParser::inlineCall):
(JSC::DFG::ByteCodeParser::handleInlining):
(JSC::DFG::ByteCodeParser::handleGetById):
(JSC::DFG::ByteCodeParser::handlePutById):
(JSC::DFG::ByteCodeParser::parseBlock):
* dfg/DFGCFGSimplificationPhase.cpp:
(JSC::DFG::CFGSimplificationPhase::run):
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGClobbersExitState.cpp: Added.
(JSC::DFG::clobbersExitState):
* dfg/DFGClobbersExitState.h: Added.
* dfg/DFGConstantFoldingPhase.cpp:
(JSC::DFG::ConstantFoldingPhase::emitPutByOffset):
* dfg/DFGDoesGC.cpp:
(JSC::DFG::doesGC):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
(JSC::DFG::FixupPhase::convertStringAddUse):
(JSC::DFG::FixupPhase::attemptToMakeFastStringAdd):
(JSC::DFG::FixupPhase::fixupGetAndSetLocalsInBlock):
(JSC::DFG::FixupPhase::fixupChecksInBlock):
* dfg/DFGFlushFormat.h:
(JSC::DFG::useKindFor):
(JSC::DFG::uncheckedUseKindFor):
(JSC::DFG::typeFilterFor):
* dfg/DFGGraph.cpp:
(JSC::DFG::printWhiteSpace):
(JSC::DFG::Graph::dumpCodeOrigin):
(JSC::DFG::Graph::dump):
* dfg/DFGGraph.h:
(JSC::DFG::Graph::addSpeculationMode):
* dfg/DFGInsertionSet.cpp:
(JSC::DFG::InsertionSet::insertSlow):
(JSC::DFG::InsertionSet::execute):
* dfg/DFGLoopPreHeaderCreationPhase.cpp:
(JSC::DFG::LoopPreHeaderCreationPhase::run):
* dfg/DFGMayExit.cpp:
(JSC::DFG::mayExit):
(WTF::printInternal):
* dfg/DFGMayExit.h:
* dfg/DFGMovHintRemovalPhase.cpp:
* dfg/DFGNodeOrigin.cpp: Added.
(JSC::DFG::NodeOrigin::dump):
* dfg/DFGNodeOrigin.h:
(JSC::DFG::NodeOrigin::NodeOrigin):
(JSC::DFG::NodeOrigin::isSet):
(JSC::DFG::NodeOrigin::withSemantic):
(JSC::DFG::NodeOrigin::withExitOK):
(JSC::DFG::NodeOrigin::withInvalidExit):
(JSC::DFG::NodeOrigin::takeValidExit):
(JSC::DFG::NodeOrigin::forInsertingAfter):
(JSC::DFG::NodeOrigin::operator==):
(JSC::DFG::NodeOrigin::operator!=):
* dfg/DFGNodeType.h:
* dfg/DFGOSREntrypointCreationPhase.cpp:
(JSC::DFG::OSREntrypointCreationPhase::run):
* dfg/DFGOSRExit.cpp:
(JSC::DFG::OSRExit::OSRExit):
(JSC::DFG::OSRExit::setPatchableCodeOffset):
* dfg/DFGOSRExitBase.h:
* dfg/DFGObjectAllocationSinkingPhase.cpp:
* dfg/DFGPhantomInsertionPhase.cpp:
* dfg/DFGPhase.cpp:
(JSC::DFG::Phase::validate):
(JSC::DFG::Phase::beginPhase):
(JSC::DFG::Phase::endPhase):
* dfg/DFGPhase.h:
(JSC::DFG::Phase::vm):
(JSC::DFG::Phase::codeBlock):
(JSC::DFG::Phase::profiledBlock):
* dfg/DFGPredictionPropagationPhase.cpp:
(JSC::DFG::PredictionPropagationPhase::propagate):
* dfg/DFGPutStackSinkingPhase.cpp:
* dfg/DFGSSAConversionPhase.cpp:
(JSC::DFG::SSAConversionPhase::run):
* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::SpeculativeJIT):
(JSC::DFG::SpeculativeJIT::speculationCheck):
(JSC::DFG::SpeculativeJIT::emitInvalidationPoint):
(JSC::DFG::SpeculativeJIT::terminateSpeculativeExecution):
(JSC::DFG::SpeculativeJIT::compileCurrentBlock):
(JSC::DFG::SpeculativeJIT::checkArgumentTypes):
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT.h:
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGStoreBarrierInsertionPhase.cpp:
* dfg/DFGTypeCheckHoistingPhase.cpp:
(JSC::DFG::TypeCheckHoistingPhase::run):
* dfg/DFGValidate.cpp:
(JSC::DFG::Validate::validate):
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLowerDFGToLLVM.cpp:
(JSC::FTL::DFG::LowerDFGToLLVM::lower):
(JSC::FTL::DFG::LowerDFGToLLVM::compileNode):
(JSC::FTL::DFG::LowerDFGToLLVM::compileUpsilon):
(JSC::FTL::DFG::LowerDFGToLLVM::compileInvalidationPoint):
(JSC::FTL::DFG::LowerDFGToLLVM::appendOSRExit):
Source/WTF:
* wtf/Insertion.h:
(WTF::executeInsertions): Add a useful assertion. This come into play because JSC will use UINT_MAX as "invalid index", and that ought to trigger this assertion.</pre>
<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceJavaScriptCoreCMakeListstxt">trunk/Source/JavaScriptCore/CMakeLists.txt</a></li>
<li><a href="#trunkSourceJavaScriptCoreChangeLog">trunk/Source/JavaScriptCore/ChangeLog</a></li>
<li><a href="#trunkSourceJavaScriptCoreJavaScriptCorevcxprojJavaScriptCorevcxproj">trunk/Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj</a></li>
<li><a href="#trunkSourceJavaScriptCoreJavaScriptCorexcodeprojprojectpbxproj">trunk/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGAbstractHeaph">trunk/Source/JavaScriptCore/dfg/DFGAbstractHeap.h</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGAbstractInterpreterInlinesh">trunk/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGArgumentsEliminationPhasecpp">trunk/Source/JavaScriptCore/dfg/DFGArgumentsEliminationPhase.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGByteCodeParsercpp">trunk/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGCFGSimplificationPhasecpp">trunk/Source/JavaScriptCore/dfg/DFGCFGSimplificationPhase.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGClobberizeh">trunk/Source/JavaScriptCore/dfg/DFGClobberize.h</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGConstantFoldingPhasecpp">trunk/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGDoesGCcpp">trunk/Source/JavaScriptCore/dfg/DFGDoesGC.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGFixupPhasecpp">trunk/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGFlushFormath">trunk/Source/JavaScriptCore/dfg/DFGFlushFormat.h</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGGraphcpp">trunk/Source/JavaScriptCore/dfg/DFGGraph.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGGraphh">trunk/Source/JavaScriptCore/dfg/DFGGraph.h</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGInsertionSetcpp">trunk/Source/JavaScriptCore/dfg/DFGInsertionSet.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGLoopPreHeaderCreationPhasecpp">trunk/Source/JavaScriptCore/dfg/DFGLoopPreHeaderCreationPhase.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGMayExitcpp">trunk/Source/JavaScriptCore/dfg/DFGMayExit.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGMayExith">trunk/Source/JavaScriptCore/dfg/DFGMayExit.h</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGMovHintRemovalPhasecpp">trunk/Source/JavaScriptCore/dfg/DFGMovHintRemovalPhase.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGNodeOriginh">trunk/Source/JavaScriptCore/dfg/DFGNodeOrigin.h</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGNodeTypeh">trunk/Source/JavaScriptCore/dfg/DFGNodeType.h</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGOSREntrypointCreationPhasecpp">trunk/Source/JavaScriptCore/dfg/DFGOSREntrypointCreationPhase.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGOSRExitcpp">trunk/Source/JavaScriptCore/dfg/DFGOSRExit.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGOSRExitBaseh">trunk/Source/JavaScriptCore/dfg/DFGOSRExitBase.h</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGObjectAllocationSinkingPhasecpp">trunk/Source/JavaScriptCore/dfg/DFGObjectAllocationSinkingPhase.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGPhantomInsertionPhasecpp">trunk/Source/JavaScriptCore/dfg/DFGPhantomInsertionPhase.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGPhasecpp">trunk/Source/JavaScriptCore/dfg/DFGPhase.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGPhaseh">trunk/Source/JavaScriptCore/dfg/DFGPhase.h</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGPredictionPropagationPhasecpp">trunk/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGPutStackSinkingPhasecpp">trunk/Source/JavaScriptCore/dfg/DFGPutStackSinkingPhase.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGSSAConversionPhasecpp">trunk/Source/JavaScriptCore/dfg/DFGSSAConversionPhase.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGSafeToExecuteh">trunk/Source/JavaScriptCore/dfg/DFGSafeToExecute.h</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGSpeculativeJITcpp">trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGSpeculativeJITh">trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGSpeculativeJIT32_64cpp">trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGSpeculativeJIT64cpp">trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGStoreBarrierInsertionPhasecpp">trunk/Source/JavaScriptCore/dfg/DFGStoreBarrierInsertionPhase.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGTypeCheckHoistingPhasecpp">trunk/Source/JavaScriptCore/dfg/DFGTypeCheckHoistingPhase.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGValidatecpp">trunk/Source/JavaScriptCore/dfg/DFGValidate.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreftlFTLCapabilitiescpp">trunk/Source/JavaScriptCore/ftl/FTLCapabilities.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreftlFTLLowerDFGToLLVMcpp">trunk/Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp</a></li>
<li><a href="#trunkSourceWTFChangeLog">trunk/Source/WTF/ChangeLog</a></li>
<li><a href="#trunkSourceWTFwtfInsertionh">trunk/Source/WTF/wtf/Insertion.h</a></li>
</ul>
<h3>Added Paths</h3>
<ul>
<li><a href="#trunkSourceJavaScriptCoredfgDFGClobbersExitStatecpp">trunk/Source/JavaScriptCore/dfg/DFGClobbersExitState.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGClobbersExitStateh">trunk/Source/JavaScriptCore/dfg/DFGClobbersExitState.h</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGNodeOrigincpp">trunk/Source/JavaScriptCore/dfg/DFGNodeOrigin.cpp</a></li>
</ul>
</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceJavaScriptCoreCMakeListstxt"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/CMakeLists.txt (188978 => 188979)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/CMakeLists.txt        2015-08-26 19:21:19 UTC (rev 188978)
+++ trunk/Source/JavaScriptCore/CMakeLists.txt        2015-08-26 19:24:41 UTC (rev 188979)
</span><span class="lines">@@ -157,6 +157,7 @@
</span><span class="cx"> dfg/DFGCleanUpPhase.cpp
</span><span class="cx"> dfg/DFGClobberSet.cpp
</span><span class="cx"> dfg/DFGClobberize.cpp
</span><ins>+ dfg/DFGClobbersExitState.cpp
</ins><span class="cx"> dfg/DFGCombinedLiveness.cpp
</span><span class="cx"> dfg/DFGCommon.cpp
</span><span class="cx"> dfg/DFGCommonData.cpp
</span><span class="lines">@@ -210,6 +211,7 @@
</span><span class="cx"> dfg/DFGNaturalLoops.cpp
</span><span class="cx"> dfg/DFGNode.cpp
</span><span class="cx"> dfg/DFGNodeFlags.cpp
</span><ins>+ dfg/DFGNodeOrigin.cpp
</ins><span class="cx"> dfg/DFGOSRAvailabilityAnalysisPhase.cpp
</span><span class="cx"> dfg/DFGOSREntry.cpp
</span><span class="cx"> dfg/DFGOSREntrypointCreationPhase.cpp
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ChangeLog (188978 => 188979)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ChangeLog        2015-08-26 19:21:19 UTC (rev 188978)
+++ trunk/Source/JavaScriptCore/ChangeLog        2015-08-26 19:24:41 UTC (rev 188979)
</span><span class="lines">@@ -1,3 +1,214 @@
</span><ins>+2015-08-26 Filip Pizlo <fpizlo@apple.com>
+
+ Node::origin should be able to tell you if it's OK to exit
+ https://bugs.webkit.org/show_bug.cgi?id=145204
+
+ Reviewed by Geoffrey Garen.
+
+ This is a major change to DFG IR, that makes it easier to reason about where nodes with
+ speculations can be soundly hoisted.
+
+ A program in DFG IR is a sequence of operations that compute the values of SSA variables,
+ perform effects on the heap or stack, and perform updates to the OSR exit state. Because
+ effects and OSR exit updates are interleaved, there are points in execution where exiting
+ simply won't work. For example, we may have some bytecode operation:
+
+ [ 24] op_foo loc42 // does something, and puts a value in loc42.
+
+ that gets compiled down to a sequence of DFG IR nodes like:
+
+ a: Foo(W:Heap, R:World, bc#24) // writes heap, reads world - i.e. an observable effect.
+ b: MovHint(@a, loc42, bc#24)
+ c: SetLocal(Check:Int32:@a, loc42, bc#24, exit: bc#26)
+
+ Note that we can OSR exit at @a because we haven't yet performed any effects for bc#24 yet and
+ we have performed all effects for prior bytecode operations. That's what the origin.forExit
+ being set to "bc#24" guarantees. So, an OSR exit at @a would transfer execution to bc#24 and
+ this would not be observable. But at @b, if we try to exit to bc#24 as indicated by forExit, we
+ would end up causing the side effect of bc#24 to execute a second time. This would be
+ observable, so we cannot do it. And we cannot exit to the next instruction - bc#26 - either,
+ because @b is responsible for updating the OSR state to indicate that the result of @a should
+ be put into loc42. It's not until we get to @c that we can exit again.
+
+ This is a confusing, but useful, property of DFG IR. It's useful because it allows us to use IR
+ to spell out how we would have affected the bytecode state, and we use this to implement hard
+ things like object allocation elimination, where we use IR instructions to indicate what object
+ allocation and mutation operations we would have performed, and which bytecode variables would
+ have pointed to those objects. So long as IR allows us to describe how OSR exit state is
+ updated, there will be points in execution where that state is invalid - especially if the IR
+ to update exit state is separate from the IR to perform actual effects.
+
+ But this property is super confusing! It's difficult to explain that somehow magically, @b is a
+ bad place to put OSR exits, and that magically we will only have OSR exits at @a. Of course, it
+ all kind of makes sense - we insert OSR exit checks in phases that *know* where it's safe to
+ exit - but it's just too opaque. This also gets in the way of more sophisticated
+ transformations. For example, LICM barely works - it magically knows that loop pre-headers are
+ good places to exit from, but it has no way of determining if that is actually true. It would
+ be odd to introduce a restriction that anytime some block qualifies as a pre-header according
+ to our loop calculator, it must end with a terminal at which it is OK to exit. So, our choices
+ are to either leave LICM in a magical state and exercise extreme caution when introducing new
+ optimizations that hoist checks, or to do something to make the "can I exit here" property more
+ explicit in IR.
+
+ We have already, in a separate change, added a NodeOrigin::exitOK property, though it didn't do
+ anything yet. This change puts exitOK to work, and makes it an integral part of IR. The key
+ intuition behind this change is that if we know which nodes clobber exit state - i.e. after the
+ node, it's no longer possible to OSR exit until the exit state is fixed up - then we can figure
+ out where it's fine to exit. This change mostly adopts the already implicit rule that it's
+ always safe to exit right at the boundary of exit origins (in between two nodes where
+ origin.forExit differs), and adds a new node, called ExitOK, which is a kind of declaration
+ that exit state is good again. When making this change, I struggled with the question of
+ whether to make origin.exitOK be explicit, or something that we can compute with an analysis.
+ Of course if we are armed with a clobbersExitState(Node*) function, we can find the places
+ where it's fine to exit. But this kind of computation could get quite sophisticated if the
+ nodes belonging to an exit origin are lowered to a control-flow construct. It would also be
+ harder to see what the original intent was, if we found an error: is the bug that we shouldn't
+ be clobbering exit state, or that we shouldn't be exiting? This change opts to make exitOK be
+ an explicit property of IR, so that DFG IR validation will reject any program where exitOK is
+ true after a node that clobbersExitState(), or if exitOK is true after a node has exitOK set to
+ false - unless the latter node has a different exit origin or is an ExitOK node. It will also
+ reject any program where a node mayExit() with !exitOK.
+
+ It turns out that this revealed a lot of sloppiness and what almost looked like an outright
+ bug: the callee property of an inline closure call frame was being set up "as if" by the
+ callee's op_enter. If we did hoist a check per the old rule - to the boundary of exit origins -
+ then we would crash because the callee is unknown. It also revealed that LICM could *almost*
+ get hosed by having a pre-header where there are effects before the jump. I wasn't able to
+ construct a test case that would crash trunk, but I also couldn't quite prove why such a
+ program couldn't be constructed. I did fix the issue in loop pre-header creation, and the
+ validater does catch the issue because of its exitOK assertions.
+
+ This doesn't yet add any other safeguards to LICM - that phase still expects that pre-headers
+ are in place and that they were created in such a way that their terminal origins have exitOK.
+ It also still keeps the old way of saying "not OK to exit" - having a clear NodeOrigin. In a
+ later patch I'll remove that and use !exitOK everywhere. Note that I did consider using clear
+ NodeOrigins to signify that it's not OK to exit, but that would make DFGForAllKills a lot more
+ expensive - it would have to sometimes search to find nearby forExit origins if the current
+ node doesn't have it set - and that's a critical phase for DFG compilation performance.
+ Requiring that forExit is usually set to *something* and that properly shadows the original
+ bytecode is cheap and easy, so it seemed like a good trade-off.
+
+ This change has no performance effect. Its only effect is that it makes the compiler easier to
+ understand by turning a previously magical concept into an explicit one.
+
+ * CMakeLists.txt:
+ * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj:
+ * JavaScriptCore.xcodeproj/project.pbxproj:
+ * dfg/DFGAbstractHeap.h:
+ * dfg/DFGAbstractInterpreterInlines.h:
+ (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+ * dfg/DFGArgumentsEliminationPhase.cpp:
+ * dfg/DFGByteCodeParser.cpp:
+ (JSC::DFG::ByteCodeParser::setDirect):
+ (JSC::DFG::ByteCodeParser::currentNodeOrigin):
+ (JSC::DFG::ByteCodeParser::branchData):
+ (JSC::DFG::ByteCodeParser::addToGraph):
+ (JSC::DFG::ByteCodeParser::handleCall):
+ (JSC::DFG::ByteCodeParser::inlineCall):
+ (JSC::DFG::ByteCodeParser::handleInlining):
+ (JSC::DFG::ByteCodeParser::handleGetById):
+ (JSC::DFG::ByteCodeParser::handlePutById):
+ (JSC::DFG::ByteCodeParser::parseBlock):
+ * dfg/DFGCFGSimplificationPhase.cpp:
+ (JSC::DFG::CFGSimplificationPhase::run):
+ * dfg/DFGClobberize.h:
+ (JSC::DFG::clobberize):
+ * dfg/DFGClobbersExitState.cpp: Added.
+ (JSC::DFG::clobbersExitState):
+ * dfg/DFGClobbersExitState.h: Added.
+ * dfg/DFGConstantFoldingPhase.cpp:
+ (JSC::DFG::ConstantFoldingPhase::emitPutByOffset):
+ * dfg/DFGDoesGC.cpp:
+ (JSC::DFG::doesGC):
+ * dfg/DFGFixupPhase.cpp:
+ (JSC::DFG::FixupPhase::fixupNode):
+ (JSC::DFG::FixupPhase::convertStringAddUse):
+ (JSC::DFG::FixupPhase::attemptToMakeFastStringAdd):
+ (JSC::DFG::FixupPhase::fixupGetAndSetLocalsInBlock):
+ (JSC::DFG::FixupPhase::fixupChecksInBlock):
+ * dfg/DFGFlushFormat.h:
+ (JSC::DFG::useKindFor):
+ (JSC::DFG::uncheckedUseKindFor):
+ (JSC::DFG::typeFilterFor):
+ * dfg/DFGGraph.cpp:
+ (JSC::DFG::printWhiteSpace):
+ (JSC::DFG::Graph::dumpCodeOrigin):
+ (JSC::DFG::Graph::dump):
+ * dfg/DFGGraph.h:
+ (JSC::DFG::Graph::addSpeculationMode):
+ * dfg/DFGInsertionSet.cpp:
+ (JSC::DFG::InsertionSet::insertSlow):
+ (JSC::DFG::InsertionSet::execute):
+ * dfg/DFGLoopPreHeaderCreationPhase.cpp:
+ (JSC::DFG::LoopPreHeaderCreationPhase::run):
+ * dfg/DFGMayExit.cpp:
+ (JSC::DFG::mayExit):
+ (WTF::printInternal):
+ * dfg/DFGMayExit.h:
+ * dfg/DFGMovHintRemovalPhase.cpp:
+ * dfg/DFGNodeOrigin.cpp: Added.
+ (JSC::DFG::NodeOrigin::dump):
+ * dfg/DFGNodeOrigin.h:
+ (JSC::DFG::NodeOrigin::NodeOrigin):
+ (JSC::DFG::NodeOrigin::isSet):
+ (JSC::DFG::NodeOrigin::withSemantic):
+ (JSC::DFG::NodeOrigin::withExitOK):
+ (JSC::DFG::NodeOrigin::withInvalidExit):
+ (JSC::DFG::NodeOrigin::takeValidExit):
+ (JSC::DFG::NodeOrigin::forInsertingAfter):
+ (JSC::DFG::NodeOrigin::operator==):
+ (JSC::DFG::NodeOrigin::operator!=):
+ * dfg/DFGNodeType.h:
+ * dfg/DFGOSREntrypointCreationPhase.cpp:
+ (JSC::DFG::OSREntrypointCreationPhase::run):
+ * dfg/DFGOSRExit.cpp:
+ (JSC::DFG::OSRExit::OSRExit):
+ (JSC::DFG::OSRExit::setPatchableCodeOffset):
+ * dfg/DFGOSRExitBase.h:
+ * dfg/DFGObjectAllocationSinkingPhase.cpp:
+ * dfg/DFGPhantomInsertionPhase.cpp:
+ * dfg/DFGPhase.cpp:
+ (JSC::DFG::Phase::validate):
+ (JSC::DFG::Phase::beginPhase):
+ (JSC::DFG::Phase::endPhase):
+ * dfg/DFGPhase.h:
+ (JSC::DFG::Phase::vm):
+ (JSC::DFG::Phase::codeBlock):
+ (JSC::DFG::Phase::profiledBlock):
+ * dfg/DFGPredictionPropagationPhase.cpp:
+ (JSC::DFG::PredictionPropagationPhase::propagate):
+ * dfg/DFGPutStackSinkingPhase.cpp:
+ * dfg/DFGSSAConversionPhase.cpp:
+ (JSC::DFG::SSAConversionPhase::run):
+ * dfg/DFGSafeToExecute.h:
+ (JSC::DFG::safeToExecute):
+ * dfg/DFGSpeculativeJIT.cpp:
+ (JSC::DFG::SpeculativeJIT::SpeculativeJIT):
+ (JSC::DFG::SpeculativeJIT::speculationCheck):
+ (JSC::DFG::SpeculativeJIT::emitInvalidationPoint):
+ (JSC::DFG::SpeculativeJIT::terminateSpeculativeExecution):
+ (JSC::DFG::SpeculativeJIT::compileCurrentBlock):
+ (JSC::DFG::SpeculativeJIT::checkArgumentTypes):
+ (JSC::DFG::SpeculativeJIT::compile):
+ * dfg/DFGSpeculativeJIT.h:
+ * dfg/DFGSpeculativeJIT32_64.cpp:
+ (JSC::DFG::SpeculativeJIT::compile):
+ * dfg/DFGSpeculativeJIT64.cpp:
+ (JSC::DFG::SpeculativeJIT::compile):
+ * dfg/DFGStoreBarrierInsertionPhase.cpp:
+ * dfg/DFGTypeCheckHoistingPhase.cpp:
+ (JSC::DFG::TypeCheckHoistingPhase::run):
+ * dfg/DFGValidate.cpp:
+ (JSC::DFG::Validate::validate):
+ * ftl/FTLCapabilities.cpp:
+ (JSC::FTL::canCompile):
+ * ftl/FTLLowerDFGToLLVM.cpp:
+ (JSC::FTL::DFG::LowerDFGToLLVM::lower):
+ (JSC::FTL::DFG::LowerDFGToLLVM::compileNode):
+ (JSC::FTL::DFG::LowerDFGToLLVM::compileUpsilon):
+ (JSC::FTL::DFG::LowerDFGToLLVM::compileInvalidationPoint):
+ (JSC::FTL::DFG::LowerDFGToLLVM::appendOSRExit):
+
</ins><span class="cx"> 2015-08-26 Andreas Kling <akling@apple.com>
</span><span class="cx">
</span><span class="cx"> [JSC] StructureTransitionTable should eagerly deallocate single-transition WeakImpls.
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreJavaScriptCorevcxprojJavaScriptCorevcxproj"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj (188978 => 188979)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj        2015-08-26 19:21:19 UTC (rev 188978)
+++ trunk/Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj        2015-08-26 19:24:41 UTC (rev 188979)
</span><span class="lines">@@ -391,6 +391,7 @@
</span><span class="cx"> <ClCompile Include="..\dfg\DFGCleanUpPhase.cpp" />
</span><span class="cx"> <ClCompile Include="..\dfg\DFGClobberize.cpp" />
</span><span class="cx"> <ClCompile Include="..\dfg\DFGClobberSet.cpp" />
</span><ins>+ <ClCompile Include="..\dfg\DFGClobbersExitState.cpp" />
</ins><span class="cx"> <ClCompile Include="..\dfg\DFGCombinedLiveness.cpp" />
</span><span class="cx"> <ClCompile Include="..\dfg\DFGCommon.cpp" />
</span><span class="cx"> <ClCompile Include="..\dfg\DFGCommonData.cpp" />
</span><span class="lines">@@ -446,6 +447,7 @@
</span><span class="cx"> <ClCompile Include="..\dfg\DFGNaturalLoops.cpp" />
</span><span class="cx"> <ClCompile Include="..\dfg\DFGNode.cpp" />
</span><span class="cx"> <ClCompile Include="..\dfg\DFGNodeFlags.cpp" />
</span><ins>+ <ClCompile Include="..\dfg\DFGNodeOrigin.cpp" />
</ins><span class="cx"> <ClCompile Include="..\dfg\DFGOperations.cpp" />
</span><span class="cx"> <ClCompile Include="..\dfg\DFGOSRAvailabilityAnalysisPhase.cpp" />
</span><span class="cx"> <ClCompile Include="..\dfg\DFGOSREntry.cpp" />
</span><span class="lines">@@ -1108,6 +1110,7 @@
</span><span class="cx"> <ClInclude Include="..\dfg\DFGCleanUpPhase.h" />
</span><span class="cx"> <ClInclude Include="..\dfg\DFGClobberize.h" />
</span><span class="cx"> <ClInclude Include="..\dfg\DFGClobberSet.h" />
</span><ins>+ <ClInclude Include="..\dfg\DFGClobbersExitState.h" />
</ins><span class="cx"> <ClInclude Include="..\dfg\DFGCombinedLiveness.h" />
</span><span class="cx"> <ClInclude Include="..\dfg\DFGCommon.h" />
</span><span class="cx"> <ClInclude Include="..\dfg\DFGCommonData.h" />
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreJavaScriptCorexcodeprojprojectpbxproj"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj (188978 => 188979)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj        2015-08-26 19:21:19 UTC (rev 188978)
+++ trunk/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj        2015-08-26 19:24:41 UTC (rev 188979)
</span><span class="lines">@@ -296,6 +296,8 @@
</span><span class="cx">                 0F3B7E2A19A11B8000D9BC56 /* CallVariant.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F3B7E2419A11B8000D9BC56 /* CallVariant.cpp */; };
</span><span class="cx">                 0F3B7E2B19A11B8000D9BC56 /* CallVariant.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F3B7E2519A11B8000D9BC56 /* CallVariant.h */; settings = {ATTRIBUTES = (Private, ); }; };
</span><span class="cx">                 0F3BD1B71B896A0700598AA6 /* DFGInsertionSet.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F3BD1B61B896A0700598AA6 /* DFGInsertionSet.cpp */; };
</span><ins>+                0F3C1F1A1B868E7900ABB08B /* DFGClobbersExitState.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F3C1F181B868E7900ABB08B /* DFGClobbersExitState.cpp */; };
+                0F3C1F1B1B868E7900ABB08B /* DFGClobbersExitState.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F3C1F191B868E7900ABB08B /* DFGClobbersExitState.h */; settings = {ATTRIBUTES = (Private, ); }; };
</ins><span class="cx">                 0F3E01AA19D353A500F61B7F /* DFGPrePostNumbering.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F3E01A819D353A500F61B7F /* DFGPrePostNumbering.cpp */; };
</span><span class="cx">                 0F3E01AB19D353A500F61B7F /* DFGPrePostNumbering.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F3E01A919D353A500F61B7F /* DFGPrePostNumbering.h */; settings = {ATTRIBUTES = (Private, ); }; };
</span><span class="cx">                 0F426A481460CBB300131F8F /* ValueRecovery.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F426A451460CBAB00131F8F /* ValueRecovery.h */; settings = {ATTRIBUTES = (Private, ); }; };
</span><span class="lines">@@ -346,6 +348,7 @@
</span><span class="cx">                 0F5A52D017ADD717008ECB2D /* CopyToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F5A52CF17ADD717008ECB2D /* CopyToken.h */; settings = {ATTRIBUTES = (Private, ); }; };
</span><span class="cx">                 0F5A6283188C98D40072C9DF /* FTLValueRange.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F5A6281188C98D40072C9DF /* FTLValueRange.cpp */; };
</span><span class="cx">                 0F5A6284188C98D40072C9DF /* FTLValueRange.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F5A6282188C98D40072C9DF /* FTLValueRange.h */; settings = {ATTRIBUTES = (Private, ); }; };
</span><ins>+                0F5D085D1B8CF99D001143B4 /* DFGNodeOrigin.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F5D085C1B8CF99D001143B4 /* DFGNodeOrigin.cpp */; };
</ins><span class="cx">                 0F5EF91E16878F7A003E5C25 /* JITThunks.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F5EF91B16878F78003E5C25 /* JITThunks.cpp */; };
</span><span class="cx">                 0F5EF91F16878F7D003E5C25 /* JITThunks.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F5EF91C16878F78003E5C25 /* JITThunks.h */; settings = {ATTRIBUTES = (Private, ); }; };
</span><span class="cx">                 0F5F08CF146C7633000472A9 /* UnconditionalFinalizer.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F5F08CE146C762F000472A9 /* UnconditionalFinalizer.h */; settings = {ATTRIBUTES = (Private, ); }; };
</span><span class="lines">@@ -2108,6 +2111,8 @@
</span><span class="cx">                 0F3B7E2419A11B8000D9BC56 /* CallVariant.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CallVariant.cpp; sourceTree = "<group>"; };
</span><span class="cx">                 0F3B7E2519A11B8000D9BC56 /* CallVariant.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CallVariant.h; sourceTree = "<group>"; };
</span><span class="cx">                 0F3BD1B61B896A0700598AA6 /* DFGInsertionSet.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DFGInsertionSet.cpp; path = dfg/DFGInsertionSet.cpp; sourceTree = "<group>"; };
</span><ins>+                0F3C1F181B868E7900ABB08B /* DFGClobbersExitState.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DFGClobbersExitState.cpp; path = dfg/DFGClobbersExitState.cpp; sourceTree = "<group>"; };
+                0F3C1F191B868E7900ABB08B /* DFGClobbersExitState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGClobbersExitState.h; path = dfg/DFGClobbersExitState.h; sourceTree = "<group>"; };
</ins><span class="cx">                 0F3E01A819D353A500F61B7F /* DFGPrePostNumbering.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DFGPrePostNumbering.cpp; path = dfg/DFGPrePostNumbering.cpp; sourceTree = "<group>"; };
</span><span class="cx">                 0F3E01A919D353A500F61B7F /* DFGPrePostNumbering.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGPrePostNumbering.h; path = dfg/DFGPrePostNumbering.h; sourceTree = "<group>"; };
</span><span class="cx">                 0F426A451460CBAB00131F8F /* ValueRecovery.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ValueRecovery.h; sourceTree = "<group>"; };
</span><span class="lines">@@ -2159,6 +2164,7 @@
</span><span class="cx">                 0F5A52CF17ADD717008ECB2D /* CopyToken.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CopyToken.h; sourceTree = "<group>"; };
</span><span class="cx">                 0F5A6281188C98D40072C9DF /* FTLValueRange.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FTLValueRange.cpp; path = ftl/FTLValueRange.cpp; sourceTree = "<group>"; };
</span><span class="cx">                 0F5A6282188C98D40072C9DF /* FTLValueRange.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FTLValueRange.h; path = ftl/FTLValueRange.h; sourceTree = "<group>"; };
</span><ins>+                0F5D085C1B8CF99D001143B4 /* DFGNodeOrigin.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DFGNodeOrigin.cpp; path = dfg/DFGNodeOrigin.cpp; sourceTree = "<group>"; };
</ins><span class="cx">                 0F5EF91B16878F78003E5C25 /* JITThunks.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JITThunks.cpp; sourceTree = "<group>"; };
</span><span class="cx">                 0F5EF91C16878F78003E5C25 /* JITThunks.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JITThunks.h; sourceTree = "<group>"; };
</span><span class="cx">                 0F5F08CE146C762F000472A9 /* UnconditionalFinalizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UnconditionalFinalizer.h; sourceTree = "<group>"; };
</span><span class="lines">@@ -5119,6 +5125,8 @@
</span><span class="cx">                                 A77A423917A0BBFD00A8DB81 /* DFGClobberize.h */,
</span><span class="cx">                                 A77A423A17A0BBFD00A8DB81 /* DFGClobberSet.cpp */,
</span><span class="cx">                                 A77A423B17A0BBFD00A8DB81 /* DFGClobberSet.h */,
</span><ins>+                                0F3C1F181B868E7900ABB08B /* DFGClobbersExitState.cpp */,
+                                0F3C1F191B868E7900ABB08B /* DFGClobbersExitState.h */,
</ins><span class="cx">                                 0F04396B1B03DC0B009598B7 /* DFGCombinedLiveness.cpp */,
</span><span class="cx">                                 0F04396C1B03DC0B009598B7 /* DFGCombinedLiveness.h */,
</span><span class="cx">                                 0FB4B51A16B62772003F696B /* DFGCommon.cpp */,
</span><span class="lines">@@ -5240,6 +5248,7 @@
</span><span class="cx">                                 0FB4B51F16B62772003F696B /* DFGNodeAllocator.h */,
</span><span class="cx">                                 0FA581B7150E952A00B9A2D9 /* DFGNodeFlags.cpp */,
</span><span class="cx">                                 0FA581B8150E952A00B9A2D9 /* DFGNodeFlags.h */,
</span><ins>+                                0F5D085C1B8CF99D001143B4 /* DFGNodeOrigin.cpp */,
</ins><span class="cx">                                 0F300B7718AB051100A6D72E /* DFGNodeOrigin.h */,
</span><span class="cx">                                 0FA581B9150E952A00B9A2D9 /* DFGNodeType.h */,
</span><span class="cx">                                 0F2B9CDA19D0BA7D00B1D1B5 /* DFGObjectAllocationSinkingPhase.cpp */,
</span><span class="lines">@@ -6148,6 +6157,7 @@
</span><span class="cx">                                 0FFFC95E14EF90B700C72532 /* DFGPredictionPropagationPhase.h in Headers */,
</span><span class="cx">                                 86EC9DD11328DF82002B2AD7 /* DFGRegisterBank.h in Headers */,
</span><span class="cx">                                 0F2FCCFC18A60070001A27F8 /* DFGSafepoint.h in Headers */,
</span><ins>+                                0F3C1F1B1B868E7900ABB08B /* DFGClobbersExitState.h in Headers */,
</ins><span class="cx">                                 A77A424317A0BBFD00A8DB81 /* DFGSafeToExecute.h in Headers */,
</span><span class="cx">                                 A741017F179DAF80002EB8BA /* DFGSaneStringGetByValSlowPathGenerator.h in Headers */,
</span><span class="cx">                                 0F2FCCFD18A60070001A27F8 /* DFGScannable.h in Headers */,
</span><span class="lines">@@ -7381,6 +7391,7 @@
</span><span class="cx">                                 149559EE0DDCDDF700648087 /* DebuggerCallFrame.cpp in Sources */,
</span><span class="cx">                                 2A7A58EF1808A4C40020BDF7 /* DeferGC.cpp in Sources */,
</span><span class="cx">                                 0FC712DE17CD8779008CC93C /* DeferredCompilationCallback.cpp in Sources */,
</span><ins>+                                0F3C1F1A1B868E7900ABB08B /* DFGClobbersExitState.cpp in Sources */,
</ins><span class="cx">                                 A77A423D17A0BBFD00A8DB81 /* DFGAbstractHeap.cpp in Sources */,
</span><span class="cx">                                 0F55C19417276E4600CEABFD /* DFGAbstractValue.cpp in Sources */,
</span><span class="cx">                                 0F485321187750560083B687 /* DFGArithMode.cpp in Sources */,
</span><span class="lines">@@ -7534,6 +7545,7 @@
</span><span class="cx">                                 0F0332C018ADFAE1005F979A /* ExitingJITType.cpp in Sources */,
</span><span class="cx">                                 0FB105851675480F00F8AB6E /* ExitKind.cpp in Sources */,
</span><span class="cx">                                 0FEA0A1C1708B00700BB722C /* FTLAbstractHeap.cpp in Sources */,
</span><ins>+                                0F5D085D1B8CF99D001143B4 /* DFGNodeOrigin.cpp in Sources */,
</ins><span class="cx">                                 FE3913541B794F6E00EDAF71 /* LiveObjectList.cpp in Sources */,
</span><span class="cx">                                 0F978B3B1AAEA71D007C7369 /* ConstantMode.cpp in Sources */,
</span><span class="cx">                                 0FE050251AA9095600D33B33 /* ClonedArguments.cpp in Sources */,
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGAbstractHeaph"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGAbstractHeap.h (188978 => 188979)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGAbstractHeap.h        2015-08-26 19:21:19 UTC (rev 188978)
+++ trunk/Source/JavaScriptCore/dfg/DFGAbstractHeap.h        2015-08-26 19:24:41 UTC (rev 188979)
</span><span class="lines">@@ -39,6 +39,7 @@
</span><span class="cx"> // - Stack with a TOP payload is a direct subtype of World
</span><span class="cx"> // - Stack with a non-TOP payload is a direct subtype of Stack with a TOP payload.
</span><span class="cx"> // - Heap is a direct subtype of World.
</span><ins>+// - SideState is a direct subtype of World.
</ins><span class="cx"> // - Any other kind with TOP payload is the direct subtype of Heap.
</span><span class="cx"> // - Any other kind with non-TOP payload is the direct subtype of the same kind with a TOP payload.
</span><span class="cx">
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGAbstractInterpreterInlinesh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h (188978 => 188979)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h        2015-08-26 19:21:19 UTC (rev 188978)
+++ trunk/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h        2015-08-26 19:24:41 UTC (rev 188979)
</span><span class="lines">@@ -2450,6 +2450,7 @@
</span><span class="cx"> case CheckTierUpWithNestedTriggerAndOSREnter:
</span><span class="cx"> case LoopHint:
</span><span class="cx"> case ZombieHint:
</span><ins>+ case ExitOK:
</ins><span class="cx"> break;
</span><span class="cx">
</span><span class="cx"> case Unreachable:
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGArgumentsEliminationPhasecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGArgumentsEliminationPhase.cpp (188978 => 188979)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGArgumentsEliminationPhase.cpp        2015-08-26 19:21:19 UTC (rev 188978)
+++ trunk/Source/JavaScriptCore/dfg/DFGArgumentsEliminationPhase.cpp        2015-08-26 19:24:41 UTC (rev 188979)
</span><span class="lines">@@ -484,16 +484,21 @@
</span><span class="cx"> if (inlineCallFrame
</span><span class="cx"> && !inlineCallFrame->isVarargs()
</span><span class="cx"> && inlineCallFrame->arguments.size() - varargsData->offset <= varargsData->limit) {
</span><ins>+
+ // LoadVarargs can exit, so it better be exitOK.
+ DFG_ASSERT(m_graph, node, node->origin.exitOK);
+ bool canExit = true;
+
</ins><span class="cx"> Node* argumentCount = insertionSet.insertConstant(
</span><del>- nodeIndex, node->origin,
</del><ins>+ nodeIndex, node->origin.withExitOK(canExit),
</ins><span class="cx"> jsNumber(inlineCallFrame->arguments.size() - varargsData->offset));
</span><span class="cx"> insertionSet.insertNode(
</span><del>- nodeIndex, SpecNone, MovHint, node->origin,
</del><ins>+ nodeIndex, SpecNone, MovHint, node->origin.takeValidExit(canExit),
</ins><span class="cx"> OpInfo(varargsData->count.offset()), Edge(argumentCount));
</span><span class="cx"> insertionSet.insertNode(
</span><del>- nodeIndex, SpecNone, PutStack, node->origin,
</del><ins>+ nodeIndex, SpecNone, PutStack, node->origin.withExitOK(canExit),
</ins><span class="cx"> OpInfo(m_graph.m_stackAccessData.add(varargsData->count, FlushedInt32)),
</span><del>- Edge(argumentCount, Int32Use));
</del><ins>+ Edge(argumentCount, KnownInt32Use));
</ins><span class="cx">
</span><span class="cx"> DFG_ASSERT(m_graph, node, varargsData->limit - 1 >= varargsData->mandatoryMinimum);
</span><span class="cx"> // Define our limit to not include "this", since that's a bit easier to reason about.
</span><span class="lines">@@ -514,7 +519,8 @@
</span><span class="cx"> reg, FlushedJSValue);
</span><span class="cx">
</span><span class="cx"> value = insertionSet.insertNode(
</span><del>- nodeIndex, SpecNone, GetStack, node->origin, OpInfo(data));
</del><ins>+ nodeIndex, SpecNone, GetStack, node->origin.withExitOK(canExit),
+ OpInfo(data));
</ins><span class="cx"> } else {
</span><span class="cx"> // FIXME: We shouldn't have to store anything if
</span><span class="cx"> // storeIndex >= varargsData->mandatoryMinimum, but we will still
</span><span class="lines">@@ -525,7 +531,7 @@
</span><span class="cx">
</span><span class="cx"> if (!undefined) {
</span><span class="cx"> undefined = insertionSet.insertConstant(
</span><del>- nodeIndex, node->origin, jsUndefined());
</del><ins>+ nodeIndex, node->origin.withExitOK(canExit), jsUndefined());
</ins><span class="cx"> }
</span><span class="cx"> value = undefined;
</span><span class="cx"> }
</span><span class="lines">@@ -537,14 +543,15 @@
</span><span class="cx"> m_graph.m_stackAccessData.add(reg, FlushedJSValue);
</span><span class="cx">
</span><span class="cx"> insertionSet.insertNode(
</span><del>- nodeIndex, SpecNone, MovHint, node->origin, OpInfo(reg.offset()),
- Edge(value));
</del><ins>+ nodeIndex, SpecNone, MovHint, node->origin.takeValidExit(canExit),
+ OpInfo(reg.offset()), Edge(value));
</ins><span class="cx"> insertionSet.insertNode(
</span><del>- nodeIndex, SpecNone, PutStack, node->origin, OpInfo(data),
- Edge(value));
</del><ins>+ nodeIndex, SpecNone, PutStack, node->origin.withExitOK(canExit),
+ OpInfo(data), Edge(value));
</ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> node->remove();
</span><ins>+ node->origin.exitOK = canExit;
</ins><span class="cx"> break;
</span><span class="cx"> }
</span><span class="cx">
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGByteCodeParsercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp (188978 => 188979)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp        2015-08-26 19:21:19 UTC (rev 188978)
+++ trunk/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp        2015-08-26 19:24:41 UTC (rev 188979)
</span><span class="lines">@@ -35,6 +35,7 @@
</span><span class="cx"> #include "CodeBlockWithJITType.h"
</span><span class="cx"> #include "DFGArrayMode.h"
</span><span class="cx"> #include "DFGCapabilities.h"
</span><ins>+#include "DFGClobbersExitState.h"
</ins><span class="cx"> #include "DFGGraph.h"
</span><span class="cx"> #include "DFGJITCode.h"
</span><span class="cx"> #include "GetByIdStatus.h"
</span><span class="lines">@@ -362,6 +363,9 @@
</span><span class="cx"> {
</span><span class="cx"> addToGraph(MovHint, OpInfo(operand.offset()), value);
</span><span class="cx">
</span><ins>+ // We can't exit anymore because our OSR exit state has changed.
+ m_exitOK = false;
+
</ins><span class="cx"> DelayedSetLocal delayed(currentCodeOrigin(), operand, value);
</span><span class="cx">
</span><span class="cx"> if (setMode == NormalSet) {
</span><span class="lines">@@ -651,15 +655,17 @@
</span><span class="cx">
</span><span class="cx"> NodeOrigin currentNodeOrigin()
</span><span class="cx"> {
</span><del>- // FIXME: We should set the forExit origin only on those nodes that can exit.
- // https://bugs.webkit.org/show_bug.cgi?id=145204
</del><span class="cx"> CodeOrigin semantic;
</span><ins>+ CodeOrigin forExit;
+
</ins><span class="cx"> if (m_currentSemanticOrigin.isSet())
</span><span class="cx"> semantic = m_currentSemanticOrigin;
</span><span class="cx"> else
</span><span class="cx"> semantic = currentCodeOrigin();
</span><del>-
- return NodeOrigin(semantic, currentCodeOrigin(), true);
</del><ins>+
+ forExit = currentCodeOrigin();
+
+ return NodeOrigin(semantic, forExit, m_exitOK);
</ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> BranchData* branchData(unsigned taken, unsigned notTaken)
</span><span class="lines">@@ -677,6 +683,8 @@
</span><span class="cx"> if (Options::verboseDFGByteCodeParsing())
</span><span class="cx"> dataLog(" appended ", node, " ", Graph::opName(node->op()), "\n");
</span><span class="cx"> m_currentBlock->append(node);
</span><ins>+ if (clobbersExitState(m_graph, node))
+ m_exitOK = false;
</ins><span class="cx"> return node;
</span><span class="cx"> }
</span><span class="cx">
</span><span class="lines">@@ -900,6 +908,8 @@
</span><span class="cx"> unsigned m_currentIndex;
</span><span class="cx"> // The semantic origin of the current node if different from the current Index.
</span><span class="cx"> CodeOrigin m_currentSemanticOrigin;
</span><ins>+ // True if it's OK to OSR exit right now.
+ bool m_exitOK { false };
</ins><span class="cx">
</span><span class="cx"> FrozenValue* m_constantUndefined;
</span><span class="cx"> FrozenValue* m_constantNull;
</span><span class="lines">@@ -1053,6 +1063,7 @@
</span><span class="cx">
</span><span class="cx"> #define LAST_OPCODE(name) \
</span><span class="cx"> m_currentIndex += OPCODE_LENGTH(name); \
</span><ins>+ m_exitOK = false; \
</ins><span class="cx"> return shouldContinueParsing
</span><span class="cx">
</span><span class="cx"> void ByteCodeParser::handleCall(Instruction* pc, NodeType op, CodeSpecializationKind kind)
</span><span class="lines">@@ -1324,6 +1335,15 @@
</span><span class="cx"> VirtualRegister resultReg(resultOperand);
</span><span class="cx"> if (resultReg.isValid())
</span><span class="cx"> resultReg = m_inlineStackTop->remapOperand(resultReg);
</span><ins>+
+ VariableAccessData* calleeVariable = nullptr;
+ if (callee.isClosureCall()) {
+ Node* calleeSet = set(
+ VirtualRegister(registerOffset + JSStack::Callee), callTargetNode, ImmediateNakedSet);
+
+ calleeVariable = calleeSet->variableAccessData();
+ calleeVariable->mergeShouldNeverUnbox(true);
+ }
</ins><span class="cx">
</span><span class="cx"> InlineStackEntry inlineStackEntry(
</span><span class="cx"> this, codeBlock, codeBlock, m_graph.lastBlock(), callee.function(), resultReg,
</span><span class="lines">@@ -1333,6 +1353,9 @@
</span><span class="cx"> unsigned oldIndex = m_currentIndex;
</span><span class="cx"> m_currentIndex = 0;
</span><span class="cx">
</span><ins>+ // At this point, it's again OK to OSR exit.
+ m_exitOK = true;
+
</ins><span class="cx"> InlineVariableData inlineVariableData;
</span><span class="cx"> inlineVariableData.inlineCallFrame = m_inlineStackTop->m_inlineCallFrame;
</span><span class="cx"> inlineVariableData.argumentPositionStart = argumentPositionStart;
</span><span class="lines">@@ -1342,20 +1365,17 @@
</span><span class="cx"> m_inlineStackTop->m_inlineCallFrame->isClosureCall
</span><span class="cx"> == callee.isClosureCall());
</span><span class="cx"> if (callee.isClosureCall()) {
</span><del>- VariableAccessData* calleeVariable =
- set(VirtualRegister(JSStack::Callee), callTargetNode, ImmediateNakedSet)->variableAccessData();
-
- calleeVariable->mergeShouldNeverUnbox(true);
-
</del><ins>+ RELEASE_ASSERT(calleeVariable);
</ins><span class="cx"> inlineVariableData.calleeVariable = calleeVariable;
</span><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> m_graph.m_inlineVariableData.append(inlineVariableData);
</span><del>-
</del><ins>+
</ins><span class="cx"> parseCodeBlock();
</span><span class="cx"> clearCaches(); // Reset our state now that we're back to the outer code.
</span><span class="cx">
</span><span class="cx"> m_currentIndex = oldIndex;
</span><ins>+ m_exitOK = false;
</ins><span class="cx">
</span><span class="cx"> // If the inlined code created some new basic blocks, then we have linking to do.
</span><span class="cx"> if (inlineStackEntry.m_callsiteBlockHead != m_graph.lastBlock()) {
</span><span class="lines">@@ -1724,6 +1744,11 @@
</span><span class="cx"> if (verbose)
</span><span class="cx"> dataLog("Callee is going to be ", calleeReg, "\n");
</span><span class="cx"> setDirect(calleeReg, callTargetNode, ImmediateSetWithFlush);
</span><ins>+
+ // It's OK to exit right now, even though we set some locals. That's because those locals are not
+ // user-visible.
+ m_exitOK = true;
+ addToGraph(ExitOK);
</ins><span class="cx">
</span><span class="cx"> SwitchData& data = *m_graph.m_switchData.add();
</span><span class="cx"> data.kind = SwitchCell;
</span><span class="lines">@@ -1781,6 +1806,7 @@
</span><span class="cx"> }
</span><span class="cx"> data.cases.append(SwitchCase(m_graph.freeze(thingToCaseOn), block.get()));
</span><span class="cx"> m_currentIndex = nextOffset;
</span><ins>+ m_exitOK = true;
</ins><span class="cx"> processSetLocalQueue(); // This only comes into play for intrinsics, since normal inlined code will leave an empty queue.
</span><span class="cx"> addToGraph(Jump);
</span><span class="cx"> if (verbose)
</span><span class="lines">@@ -1795,6 +1821,7 @@
</span><span class="cx"> RefPtr<BasicBlock> slowPathBlock = adoptRef(
</span><span class="cx"> new BasicBlock(UINT_MAX, m_numArguments, m_numLocals, PNaN));
</span><span class="cx"> m_currentIndex = oldOffset;
</span><ins>+ m_exitOK = true;
</ins><span class="cx"> data.fallThrough = BranchTarget(slowPathBlock.get());
</span><span class="cx"> m_graph.appendBlock(slowPathBlock);
</span><span class="cx"> if (verbose)
</span><span class="lines">@@ -1816,6 +1843,7 @@
</span><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> m_currentIndex = nextOffset;
</span><ins>+ m_exitOK = true; // Origin changed, so it's fine to exit again.
</ins><span class="cx"> processSetLocalQueue();
</span><span class="cx"> addToGraph(Jump);
</span><span class="cx"> landingBlocks.append(m_currentBlock);
</span><span class="lines">@@ -1833,6 +1861,7 @@
</span><span class="cx"> landingBlocks[i]->terminal()->targetBlock() = continuationBlock.get();
</span><span class="cx">
</span><span class="cx"> m_currentIndex = oldOffset;
</span><ins>+ m_exitOK = true;
</ins><span class="cx">
</span><span class="cx"> if (verbose) {
</span><span class="cx"> dataLog("Done inlining (hard).\n");
</span><span class="lines">@@ -2714,6 +2743,10 @@
</span><span class="cx"> // shortcut.
</span><span class="cx"> int nextRegister = registerOffset + JSStack::CallFrameHeaderSize;
</span><span class="cx"> set(VirtualRegister(nextRegister++), base, ImmediateNakedSet);
</span><ins>+
+ // We've set some locals, but they are not user-visible. It's still OK to exit from here.
+ m_exitOK = true;
+ addToGraph(ExitOK);
</ins><span class="cx">
</span><span class="cx"> handleCall(
</span><span class="cx"> destinationOperand, Call, InlineCallFrame::GetterCall, OPCODE_LENGTH(op_get_by_id),
</span><span class="lines">@@ -2869,6 +2902,10 @@
</span><span class="cx"> int nextRegister = registerOffset + JSStack::CallFrameHeaderSize;
</span><span class="cx"> set(VirtualRegister(nextRegister++), base, ImmediateNakedSet);
</span><span class="cx"> set(VirtualRegister(nextRegister++), value, ImmediateNakedSet);
</span><ins>+
+ // We've set some locals, but they are not user-visible. It's still OK to exit from here.
+ m_exitOK = true;
+ addToGraph(ExitOK);
</ins><span class="cx">
</span><span class="cx"> handleCall(
</span><span class="cx"> VirtualRegister().offset(), Call, InlineCallFrame::SetterCall,
</span><span class="lines">@@ -2901,12 +2938,15 @@
</span><span class="cx"> Interpreter* interpreter = m_vm->interpreter;
</span><span class="cx"> Instruction* instructionsBegin = m_inlineStackTop->m_codeBlock->instructions().begin();
</span><span class="cx"> unsigned blockBegin = m_currentIndex;
</span><del>-
</del><ins>+
</ins><span class="cx"> // If we are the first basic block, introduce markers for arguments. This allows
</span><span class="cx"> // us to track if a use of an argument may use the actual argument passed, as
</span><span class="cx"> // opposed to using a value we set explicitly.
</span><span class="cx"> if (m_currentBlock == m_graph.block(0) && !inlineCallFrame()) {
</span><span class="cx"> m_graph.m_arguments.resize(m_numArguments);
</span><ins>+ // We will emit SetArgument nodes. They don't exit, but we're at the top of an op_enter so
+ // exitOK = true.
+ m_exitOK = true;
</ins><span class="cx"> for (unsigned argument = 0; argument < m_numArguments; ++argument) {
</span><span class="cx"> VariableAccessData* variable = newVariableAccessData(
</span><span class="cx"> virtualRegisterForArgument(argument));
</span><span class="lines">@@ -2922,6 +2962,10 @@
</span><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> while (true) {
</span><ins>+ // We're staring a new bytecode instruction. Hence, we once again have a place that we can exit
+ // to.
+ m_exitOK = true;
+
</ins><span class="cx"> processSetLocalQueue();
</span><span class="cx">
</span><span class="cx"> // Don't extend over jump destinations.
</span><span class="lines">@@ -2998,7 +3042,6 @@
</span><span class="cx">
</span><span class="cx"> FrozenValue* frozen = m_graph.freeze(cachedFunction);
</span><span class="cx"> addToGraph(CheckCell, OpInfo(frozen), callee);
</span><del>- set(VirtualRegister(currentInstruction[1].u.operand), addToGraph(JSConstant, OpInfo(frozen)));
</del><span class="cx">
</span><span class="cx"> function = static_cast<JSFunction*>(cachedFunction);
</span><span class="cx"> }
</span><span class="lines">@@ -3420,6 +3463,7 @@
</span><span class="cx"> if (!compiledAsGetById) {
</span><span class="cx"> ArrayMode arrayMode = getArrayMode(currentInstruction[4].u.arrayProfile, Array::Read);
</span><span class="cx"> Node* getByVal = addToGraph(GetByVal, OpInfo(arrayMode.asWord()), OpInfo(prediction), base, property);
</span><ins>+ m_exitOK = false; // GetByVal must be treated as if it clobbers exit state, since FixupPhase may make it generic.
</ins><span class="cx"> set(VirtualRegister(currentInstruction[1].u.operand), getByVal);
</span><span class="cx"> }
</span><span class="cx">
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGCFGSimplificationPhasecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGCFGSimplificationPhase.cpp (188978 => 188979)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGCFGSimplificationPhase.cpp        2015-08-26 19:21:19 UTC (rev 188978)
+++ trunk/Source/JavaScriptCore/dfg/DFGCFGSimplificationPhase.cpp        2015-08-26 19:24:41 UTC (rev 188979)
</span><span class="lines">@@ -241,7 +241,7 @@
</span><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> if (Options::validateGraphAtEachPhase())
</span><del>- validate(m_graph);
</del><ins>+ validate();
</ins><span class="cx"> } while (innerChanged);
</span><span class="cx">
</span><span class="cx"> return outerChanged;
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGClobberizeh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGClobberize.h (188978 => 188979)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGClobberize.h        2015-08-26 19:21:19 UTC (rev 188978)
+++ trunk/Source/JavaScriptCore/dfg/DFGClobberize.h        2015-08-26 19:24:41 UTC (rev 188979)
</span><span class="lines">@@ -278,6 +278,7 @@
</span><span class="cx">
</span><span class="cx"> case MovHint:
</span><span class="cx"> case ZombieHint:
</span><ins>+ case ExitOK:
</ins><span class="cx"> case KillStack:
</span><span class="cx"> case Upsilon:
</span><span class="cx"> case Phi:
</span><span class="lines">@@ -396,6 +397,7 @@
</span><span class="cx"> // This is pretty weird. In fact, StrCat has very limited effectfulness because we only
</span><span class="cx"> // pass it primitive values. But, right now, the compiler isn't smart enough to know this
</span><span class="cx"> // and that's probably OK.
</span><ins>+ // FIXME: https://bugs.webkit.org/show_bug.cgi?id=148443
</ins><span class="cx"> read(World);
</span><span class="cx"> write(Heap);
</span><span class="cx"> return;
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGClobbersExitStatecpp"></a>
<div class="addfile"><h4>Added: trunk/Source/JavaScriptCore/dfg/DFGClobbersExitState.cpp (0 => 188979)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGClobbersExitState.cpp         (rev 0)
+++ trunk/Source/JavaScriptCore/dfg/DFGClobbersExitState.cpp        2015-08-26 19:24:41 UTC (rev 188979)
</span><span class="lines">@@ -0,0 +1,101 @@
</span><ins>+/*
+ * 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 "DFGClobbersExitState.h"
+
+#if ENABLE(DFG_JIT)
+
+#include "DFGClobberize.h"
+#include "DFGGraph.h"
+#include "DFGNode.h"
+#include "JSCInlines.h"
+
+namespace JSC { namespace DFG {
+
+bool clobbersExitState(Graph& graph, Node* node)
+{
+ // There are certain nodes whose effect on the exit state has nothing to do with what they
+ // normally clobber.
+ switch (node->op()) {
+ case MovHint:
+ case ZombieHint:
+ case PutHint:
+ case KillStack:
+ return true;
+
+ case SetLocal:
+ case PutStack:
+ // These nodes write to the stack, but they may only do so after we have already had a MovHint
+ // for the exact same value and the same stack location. Hence, they have no further effect on
+ // exit state.
+ return false;
+
+ case ArrayifyToStructure:
+ case Arrayify:
+ case NewObject:
+ case NewRegexp:
+ case NewStringObject:
+ case PhantomNewObject:
+ case MaterializeNewObject:
+ case PhantomNewFunction:
+ case PhantomCreateActivation:
+ case MaterializeCreateActivation:
+ case CountExecution:
+ // These do clobber memory, but nothing that is observable. It may be nice to separate the
+ // heaps into those that are observable and those that aren't, but we don't do that right now.
+ // FIXME: https://bugs.webkit.org/show_bug.cgi?id=148440
+ return false;
+
+ case CreateActivation:
+ // Like above, but with the activation allocation caveat.
+ return node->castOperand<SymbolTable*>()->singletonScope()->isStillValid();
+
+ case NewArrowFunction:
+ case NewFunction:
+ // Like above, but with the JSFunction allocation caveat.
+ return node->castOperand<FunctionExecutable*>()->singletonFunction()->isStillValid();
+
+ default:
+ // For all other nodes, we just care about whether they write to something other than SideState.
+ bool result = false;
+ clobberize(
+ graph, node, NoOpClobberize(),
+ [&] (const AbstractHeap& heap) {
+ // There shouldn't be such a thing as a strict subtype of SideState. That's what allows
+ // us to use a fast != check, below.
+ ASSERT(!heap.isStrictSubtypeOf(SideState));
+
+ if (heap != SideState)
+ result = true;
+ },
+ NoOpClobberize());
+ return result;
+ }
+}
+
+} } // namespace JSC::DFG
+
+#endif // ENABLE(DFG_JIT)
</ins></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGClobbersExitStateh"></a>
<div class="addfile"><h4>Added: trunk/Source/JavaScriptCore/dfg/DFGClobbersExitState.h (0 => 188979)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGClobbersExitState.h         (rev 0)
+++ trunk/Source/JavaScriptCore/dfg/DFGClobbersExitState.h        2015-08-26 19:24:41 UTC (rev 188979)
</span><span class="lines">@@ -0,0 +1,69 @@
</span><ins>+/*
+ * 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 DFGClobbersExitState_h
+#define DFGClobbersExitState_h
+
+#if ENABLE(DFG_JIT)
+
+namespace JSC { namespace DFG {
+
+class Graph;
+struct Node;
+
+// A conservative approximation of whether the node will perform the kind of effect that would prevent
+// subsequent nodes from exiting to this node's exit origin. Exiting after an effect to that effect's
+// exit origin would cause the effect to execute a second time. Two kinds of such effects can exist:
+//
+// Observable heap or stack effect: If we perform such an effect and then exit to the same origin, that
+// effect will be executed a second time, which is incorrect.
+//
+// OSR exit state update: This doesn't do any observable side-effect, but it tells OSR exit that it
+// should recover some value as if an effect had happened. For example, a MovHint will tell OSR exit
+// that some bytecode variable now has a new value. If we exit to the exit origin of a MovHint after we
+// "execute" the MovHint, then the bytecode state will look as if we had already executed that bytecode
+// instruction. This could cause issues for example for bytecode like:
+//
+// op_add r1, r1, r2
+//
+// which will get lowered to something like:
+//
+// a: ArithAdd(...)
+// b: MovHint(@a, r1)
+//
+// If we exit to the op_add after executing the MovHint, then r1 will already contain the result of the
+// add. Then after exit we'll do the add again, and r1 will have the wrong value. Because of object
+// allocation elimination and PutStack sinking, we can also have other OSR exit updates, like
+// KillStack, PutHint, among others. They don't do anything so long as we stay in optimized code, but
+// they tell OSR exit how to reconstitute state.
+
+bool clobbersExitState(Graph&, Node*);
+
+} } // namespace JSC::DFG
+
+#endif // ENABLE(DFG_JIT)
+
+#endif // DFGClobbersExitState_h
+
</ins></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGConstantFoldingPhasecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp (188978 => 188979)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp        2015-08-26 19:21:19 UTC (rev 188978)
+++ trunk/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp        2015-08-26 19:24:41 UTC (rev 188979)
</span><span class="lines">@@ -667,6 +667,7 @@
</span><span class="cx">
</span><span class="cx"> addBaseCheck(indexInBlock, node, baseValue, variant.oldStructure());
</span><span class="cx">
</span><ins>+ node->child1().setUseKind(KnownCellUse);
</ins><span class="cx"> childEdge.setUseKind(KnownCellUse);
</span><span class="cx">
</span><span class="cx"> Transition* transition = 0;
</span><span class="lines">@@ -677,6 +678,9 @@
</span><span class="cx">
</span><span class="cx"> Edge propertyStorage;
</span><span class="cx">
</span><ins>+ DFG_ASSERT(m_graph, node, origin.exitOK);
+ bool canExit = true;
+
</ins><span class="cx"> if (isInlineOffset(variant.offset()))
</span><span class="cx"> propertyStorage = childEdge;
</span><span class="cx"> else if (!variant.reallocatesStorage()) {
</span><span class="lines">@@ -687,7 +691,7 @@
</span><span class="cx"> ASSERT(!isInlineOffset(variant.offset()));
</span><span class="cx"> Node* allocatePropertyStorage = m_insertionSet.insertNode(
</span><span class="cx"> indexInBlock, SpecNone, AllocatePropertyStorage,
</span><del>- origin, OpInfo(transition), childEdge);
</del><ins>+ origin.takeValidExit(canExit), OpInfo(transition), childEdge);
</ins><span class="cx"> propertyStorage = Edge(allocatePropertyStorage);
</span><span class="cx"> } else {
</span><span class="cx"> ASSERT(variant.oldStructureForTransition()->outOfLineCapacity());
</span><span class="lines">@@ -695,7 +699,7 @@
</span><span class="cx"> ASSERT(!isInlineOffset(variant.offset()));
</span><span class="cx">
</span><span class="cx"> Node* reallocatePropertyStorage = m_insertionSet.insertNode(
</span><del>- indexInBlock, SpecNone, ReallocatePropertyStorage, origin,
</del><ins>+ indexInBlock, SpecNone, ReallocatePropertyStorage, origin.takeValidExit(canExit),
</ins><span class="cx"> OpInfo(transition), childEdge,
</span><span class="cx"> Edge(m_insertionSet.insertNode(
</span><span class="cx"> indexInBlock, SpecNone, GetButterfly, origin, childEdge)));
</span><span class="lines">@@ -707,13 +711,15 @@
</span><span class="cx"> data.identifierNumber = identifierNumber;
</span><span class="cx">
</span><span class="cx"> node->convertToPutByOffset(data, propertyStorage);
</span><ins>+ node->origin.exitOK = canExit;
</ins><span class="cx">
</span><span class="cx"> if (variant.kind() == PutByIdVariant::Transition) {
</span><span class="cx"> // FIXME: PutStructure goes last until we fix either
</span><span class="cx"> // https://bugs.webkit.org/show_bug.cgi?id=142921 or
</span><span class="cx"> // https://bugs.webkit.org/show_bug.cgi?id=142924.
</span><span class="cx"> m_insertionSet.insertNode(
</span><del>- indexInBlock + 1, SpecNone, PutStructure, origin, OpInfo(transition), childEdge);
</del><ins>+ indexInBlock + 1, SpecNone, PutStructure, origin.withInvalidExit(), OpInfo(transition),
+ childEdge);
</ins><span class="cx"> }
</span><span class="cx"> }
</span><span class="cx">
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGDoesGCcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGDoesGC.cpp (188978 => 188979)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGDoesGC.cpp        2015-08-26 19:21:19 UTC (rev 188978)
+++ trunk/Source/JavaScriptCore/dfg/DFGDoesGC.cpp        2015-08-26 19:24:41 UTC (rev 188979)
</span><span class="lines">@@ -54,6 +54,7 @@
</span><span class="cx"> case SetLocal:
</span><span class="cx"> case MovHint:
</span><span class="cx"> case ZombieHint:
</span><ins>+ case ExitOK:
</ins><span class="cx"> case Phantom:
</span><span class="cx"> case Upsilon:
</span><span class="cx"> case Phi:
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGFixupPhasecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp (188978 => 188979)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp        2015-08-26 19:21:19 UTC (rev 188978)
+++ trunk/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp        2015-08-26 19:24:41 UTC (rev 188979)
</span><span class="lines">@@ -1362,6 +1362,7 @@
</span><span class="cx"> case LoopHint:
</span><span class="cx"> case MovHint:
</span><span class="cx"> case ZombieHint:
</span><ins>+ case ExitOK:
</ins><span class="cx"> case BottomValue:
</span><span class="cx"> case TypeOf:
</span><span class="cx"> break;
</span><span class="lines">@@ -1408,9 +1409,6 @@
</span><span class="cx"> void convertStringAddUse(Node* node, Edge& edge)
</span><span class="cx"> {
</span><span class="cx"> if (useKind == StringUse) {
</span><del>- // This preserves the binaryUseKind() invariant ot ValueAdd: ValueAdd's
- // two edges will always have identical use kinds, which makes the
- // decision process much easier.
</del><span class="cx"> observeUseKindOnNode<StringUse>(edge.node());
</span><span class="cx"> m_insertionSet.insertNode(
</span><span class="cx"> m_indexInBlock, SpecNone, Check, node->origin,
</span><span class="lines">@@ -1512,6 +1510,14 @@
</span><span class="cx">
</span><span class="cx"> bool attemptToMakeFastStringAdd(Node* node)
</span><span class="cx"> {
</span><ins>+ if (!node->origin.exitOK) {
+ // If this code cannot exit, then we should not convert it to a MakeRope, since MakeRope
+ // can exit. This arises because we think that StrCat clobbers exit state, even though it
+ // doesn't really do that.
+ // FIXME: https://bugs.webkit.org/show_bug.cgi?id=148443
+ return false;
+ }
+
</ins><span class="cx"> bool goodToGo = true;
</span><span class="cx"> m_graph.doToChildren(
</span><span class="cx"> node,
</span><span class="lines">@@ -1637,6 +1643,9 @@
</span><span class="cx"> break;
</span><span class="cx">
</span><span class="cx"> case SetLocal:
</span><ins>+ // NOTE: Any type checks we put here may get hoisted by fixupChecksInBlock(). So, if we
+ // add new type checking use kind for SetLocals, we need to modify that code as well.
+
</ins><span class="cx"> switch (variable->flushFormat()) {
</span><span class="cx"> case FlushedJSValue:
</span><span class="cx"> break;
</span><span class="lines">@@ -2110,9 +2119,20 @@
</span><span class="cx"> return;
</span><span class="cx"> ASSERT(block->isReachable);
</span><span class="cx"> m_block = block;
</span><del>- for (m_indexInBlock = 0; m_indexInBlock < block->size(); ++m_indexInBlock) {
- Node* node = block->at(m_indexInBlock);
</del><ins>+ unsigned indexForChecks = UINT_MAX;
+ NodeOrigin originForChecks;
+ for (unsigned indexInBlock = 0; indexInBlock < block->size(); ++indexInBlock) {
+ Node* node = block->at(indexInBlock);
</ins><span class="cx">
</span><ins>+ // If this is a node at which we could exit, then save its index. If nodes after this one
+ // cannot exit, then we will hoist checks to here.
+ if (node->origin.exitOK) {
+ indexForChecks = indexInBlock;
+ originForChecks = node->origin;
+ }
+
+ originForChecks = originForChecks.withSemantic(node->origin.semantic);
+
</ins><span class="cx"> // First, try to relax the representational demands of each node, in order to have
</span><span class="cx"> // fewer conversions.
</span><span class="cx"> switch (node->op()) {
</span><span class="lines">@@ -2175,21 +2195,21 @@
</span><span class="cx"> node,
</span><span class="cx"> [&] (Edge& edge) {
</span><span class="cx"> Node* result = nullptr;
</span><del>-
</del><ins>+
</ins><span class="cx"> switch (edge.useKind()) {
</span><span class="cx"> case DoubleRepUse:
</span><span class="cx"> case DoubleRepRealUse:
</span><span class="cx"> case DoubleRepMachineIntUse: {
</span><span class="cx"> if (edge->hasDoubleResult())
</span><del>- return;
</del><ins>+ break;
</ins><span class="cx">
</span><span class="cx"> if (edge->isNumberConstant()) {
</span><span class="cx"> result = m_insertionSet.insertNode(
</span><del>- m_indexInBlock, SpecBytecodeDouble, DoubleConstant, node->origin,
</del><ins>+ indexForChecks, SpecBytecodeDouble, DoubleConstant, originForChecks,
</ins><span class="cx"> OpInfo(m_graph.freeze(jsDoubleNumber(edge->asNumber()))));
</span><span class="cx"> } else if (edge->hasInt52Result()) {
</span><span class="cx"> result = m_insertionSet.insertNode(
</span><del>- m_indexInBlock, SpecInt52AsDouble, DoubleRep, node->origin,
</del><ins>+ indexForChecks, SpecInt52AsDouble, DoubleRep, originForChecks,
</ins><span class="cx"> Edge(edge.node(), Int52RepUse));
</span><span class="cx"> } else {
</span><span class="cx"> UseKind useKind;
</span><span class="lines">@@ -2201,53 +2221,90 @@
</span><span class="cx"> useKind = NotCellUse;
</span><span class="cx">
</span><span class="cx"> result = m_insertionSet.insertNode(
</span><del>- m_indexInBlock, SpecBytecodeDouble, DoubleRep, node->origin,
</del><ins>+ indexForChecks, SpecBytecodeDouble, DoubleRep, originForChecks,
</ins><span class="cx"> Edge(edge.node(), useKind));
</span><span class="cx"> }
</span><ins>+
+ edge.setNode(result);
</ins><span class="cx"> break;
</span><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> case Int52RepUse: {
</span><span class="cx"> if (edge->hasInt52Result())
</span><del>- return;
</del><ins>+ break;
</ins><span class="cx">
</span><span class="cx"> if (edge->isMachineIntConstant()) {
</span><span class="cx"> result = m_insertionSet.insertNode(
</span><del>- m_indexInBlock, SpecMachineInt, Int52Constant, node->origin,
</del><ins>+ indexForChecks, SpecMachineInt, Int52Constant, originForChecks,
</ins><span class="cx"> OpInfo(edge->constant()));
</span><span class="cx"> } else if (edge->hasDoubleResult()) {
</span><span class="cx"> result = m_insertionSet.insertNode(
</span><del>- m_indexInBlock, SpecMachineInt, Int52Rep, node->origin,
</del><ins>+ indexForChecks, SpecMachineInt, Int52Rep, originForChecks,
</ins><span class="cx"> Edge(edge.node(), DoubleRepMachineIntUse));
</span><span class="cx"> } else if (edge->shouldSpeculateInt32ForArithmetic()) {
</span><span class="cx"> result = m_insertionSet.insertNode(
</span><del>- m_indexInBlock, SpecInt32, Int52Rep, node->origin,
</del><ins>+ indexForChecks, SpecInt32, Int52Rep, originForChecks,
</ins><span class="cx"> Edge(edge.node(), Int32Use));
</span><span class="cx"> } else {
</span><span class="cx"> result = m_insertionSet.insertNode(
</span><del>- m_indexInBlock, SpecMachineInt, Int52Rep, node->origin,
</del><ins>+ indexForChecks, SpecMachineInt, Int52Rep, originForChecks,
</ins><span class="cx"> Edge(edge.node(), MachineIntUse));
</span><span class="cx"> }
</span><ins>+
+ edge.setNode(result);
</ins><span class="cx"> break;
</span><span class="cx"> }
</span><del>-
</del><ins>+
</ins><span class="cx"> default: {
</span><span class="cx"> if (!edge->hasDoubleResult() && !edge->hasInt52Result())
</span><del>- return;
</del><ins>+ break;
</ins><span class="cx">
</span><span class="cx"> if (edge->hasDoubleResult()) {
</span><span class="cx"> result = m_insertionSet.insertNode(
</span><del>- m_indexInBlock, SpecBytecodeDouble, ValueRep, node->origin,
</del><ins>+ indexForChecks, SpecBytecodeDouble, ValueRep, originForChecks,
</ins><span class="cx"> Edge(edge.node(), DoubleRepUse));
</span><span class="cx"> } else {
</span><span class="cx"> result = m_insertionSet.insertNode(
</span><del>- m_indexInBlock, SpecInt32 | SpecInt52AsDouble, ValueRep,
- node->origin, Edge(edge.node(), Int52RepUse));
</del><ins>+ indexForChecks, SpecInt32 | SpecInt52AsDouble, ValueRep,
+ originForChecks, Edge(edge.node(), Int52RepUse));
</ins><span class="cx"> }
</span><ins>+
+ edge.setNode(result);
</ins><span class="cx"> break;
</span><span class="cx"> } }
</span><del>-
- edge.setNode(result);
</del><ins>+
+ // It's remotely possible that this node cannot do type checks, but we now have a
+ // type check on this node. We don't have to handle the general form of this
+ // problem. It only arises when ByteCodeParser emits an immediate SetLocal, rather
+ // than a delayed one. So, we only worry about those checks that we may have put on
+ // a SetLocal. Note that "indexForChecks != indexInBlock" is just another way of
+ // saying "!node->origin.exitOK".
+ if (indexForChecks != indexInBlock && mayHaveTypeCheck(edge.useKind())) {
+ UseKind knownUseKind;
+
+ switch (edge.useKind()) {
+ case Int32Use:
+ knownUseKind = KnownInt32Use;
+ break;
+ case CellUse:
+ knownUseKind = KnownCellUse;
+ break;
+ case BooleanUse:
+ knownUseKind = KnownBooleanUse;
+ break;
+ default:
+ // This can only arise if we have a Check node, and in that case, we can
+ // just remove the original check.
+ DFG_ASSERT(m_graph, node, node->op() == Check);
+ knownUseKind = UntypedUse;
+ break;
+ }
+
+ m_insertionSet.insertNode(
+ indexForChecks, SpecNone, Check, originForChecks, edge);
+
+ edge.setUseKind(knownUseKind);
+ }
</ins><span class="cx"> });
</span><span class="cx"> }
</span><span class="cx">
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGFlushFormath"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGFlushFormat.h (188978 => 188979)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGFlushFormat.h        2015-08-26 19:21:19 UTC (rev 188978)
+++ trunk/Source/JavaScriptCore/dfg/DFGFlushFormat.h        2015-08-26 19:24:41 UTC (rev 188979)
</span><span class="lines">@@ -90,6 +90,28 @@
</span><span class="cx"> return UntypedUse;
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+inline UseKind uncheckedUseKindFor(FlushFormat format)
+{
+ switch (format) {
+ case DeadFlush:
+ case FlushedJSValue:
+ case ConflictingFlush:
+ return UntypedUse;
+ case FlushedCell:
+ return KnownCellUse;
+ case FlushedInt32:
+ return KnownInt32Use;
+ case FlushedInt52:
+ return Int52RepUse;
+ case FlushedDouble:
+ return DoubleRepUse;
+ case FlushedBoolean:
+ return KnownBooleanUse;
+ }
+ RELEASE_ASSERT_NOT_REACHED();
+ return UntypedUse;
+}
+
</ins><span class="cx"> inline SpeculatedType typeFilterFor(FlushFormat format)
</span><span class="cx"> {
</span><span class="cx"> return typeFilterFor(useKindFor(format));
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGGraphcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGGraph.cpp (188978 => 188979)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGGraph.cpp        2015-08-26 19:21:19 UTC (rev 188978)
+++ trunk/Source/JavaScriptCore/dfg/DFGGraph.cpp        2015-08-26 19:24:41 UTC (rev 188979)
</span><span class="lines">@@ -34,7 +34,9 @@
</span><span class="cx"> #include "CodeBlockWithJITType.h"
</span><span class="cx"> #include "DFGBlockWorklist.h"
</span><span class="cx"> #include "DFGClobberSet.h"
</span><ins>+#include "DFGClobbersExitState.h"
</ins><span class="cx"> #include "DFGJITCode.h"
</span><ins>+#include "DFGMayExit.h"
</ins><span class="cx"> #include "DFGVariableAccessDataDump.h"
</span><span class="cx"> #include "FullBytecodeLiveness.h"
</span><span class="cx"> #include "FunctionExecutableDump.h"
</span><span class="lines">@@ -101,8 +103,14 @@
</span><span class="cx"> out.print(" ");
</span><span class="cx"> }
</span><span class="cx">
</span><del>-bool Graph::dumpCodeOrigin(PrintStream& out, const char* prefix, Node* previousNode, Node* currentNode, DumpContext* context)
</del><ins>+bool Graph::dumpCodeOrigin(PrintStream& out, const char* prefix, Node*& previousNodeRef, Node* currentNode, DumpContext* context)
</ins><span class="cx"> {
</span><ins>+ if (!currentNode->origin.semantic)
+ return false;
+
+ Node* previousNode = previousNodeRef;
+ previousNodeRef = currentNode;
+
</ins><span class="cx"> if (!previousNode)
</span><span class="cx"> return false;
</span><span class="cx">
</span><span class="lines">@@ -345,12 +353,18 @@
</span><span class="cx"> out.print(comma, "R:", sortedListDump(reads.direct(), ","));
</span><span class="cx"> if (!writes.isEmpty())
</span><span class="cx"> out.print(comma, "W:", sortedListDump(writes.direct(), ","));
</span><ins>+ ExitMode exitMode = mayExit(*this, node);
+ if (exitMode != DoesNotExit)
+ out.print(comma, exitMode);
+ if (clobbersExitState(*this, node))
+ out.print(comma, "ClobbersExit");
</ins><span class="cx"> if (node->origin.isSet()) {
</span><span class="cx"> out.print(comma, "bc#", node->origin.semantic.bytecodeIndex);
</span><del>- if (node->origin.semantic != node->origin.forExit)
</del><ins>+ if (node->origin.semantic != node->origin.forExit && node->origin.forExit.isSet())
</ins><span class="cx"> out.print(comma, "exit: ", node->origin.forExit);
</span><span class="cx"> }
</span><del>-
</del><ins>+ if (!node->origin.exitOK)
+ out.print(comma, "ExitInvalid");
</ins><span class="cx"> out.print(")");
</span><span class="cx">
</span><span class="cx"> if (node->hasVariableAccessData(*this) && node->tryGetVariableAccessData())
</span><span class="lines">@@ -455,7 +469,7 @@
</span><span class="cx"> out.print(" Arguments: ", listDump(m_arguments), "\n");
</span><span class="cx"> out.print("\n");
</span><span class="cx">
</span><del>- Node* lastNode = 0;
</del><ins>+ Node* lastNode = nullptr;
</ins><span class="cx"> for (size_t b = 0; b < m_blocks.size(); ++b) {
</span><span class="cx"> BasicBlock* block = m_blocks[b].get();
</span><span class="cx"> if (!block)
</span><span class="lines">@@ -496,7 +510,6 @@
</span><span class="cx"> for (size_t i = 0; i < block->size(); ++i) {
</span><span class="cx"> dumpCodeOrigin(out, "", lastNode, block->at(i), context);
</span><span class="cx"> dump(out, "", block->at(i), context);
</span><del>- lastNode = block->at(i);
</del><span class="cx"> }
</span><span class="cx"> out.print(" States: ", block->cfaBranchDirection, ", ", block->cfaStructureClobberStateAtTail);
</span><span class="cx"> if (!block->cfaDidFinish)
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGGraphh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGGraph.h (188978 => 188979)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGGraph.h        2015-08-26 19:21:19 UTC (rev 188978)
+++ trunk/Source/JavaScriptCore/dfg/DFGGraph.h        2015-08-26 19:24:41 UTC (rev 188979)
</span><span class="lines">@@ -210,7 +210,7 @@
</span><span class="cx">
</span><span class="cx"> // Dump the code origin of the given node as a diff from the code origin of the
</span><span class="cx"> // preceding node. Returns true if anything was printed.
</span><del>- bool dumpCodeOrigin(PrintStream&, const char* prefix, Node* previousNode, Node* currentNode, DumpContext*);
</del><ins>+ bool dumpCodeOrigin(PrintStream&, const char* prefix, Node*& previousNode, Node* currentNode, DumpContext*);
</ins><span class="cx">
</span><span class="cx"> AddSpeculationMode addSpeculationMode(Node* add, bool leftShouldSpeculateInt32, bool rightShouldSpeculateInt32, PredictionPass pass)
</span><span class="cx"> {
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGInsertionSetcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGInsertionSet.cpp (188978 => 188979)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGInsertionSet.cpp        2015-08-26 19:21:19 UTC (rev 188978)
+++ trunk/Source/JavaScriptCore/dfg/DFGInsertionSet.cpp        2015-08-26 19:24:41 UTC (rev 188979)
</span><span class="lines">@@ -42,7 +42,7 @@
</span><span class="cx"> }
</span><span class="cx"> }
</span><span class="cx">
</span><del>- RELEASE_ASSERT_NOT_REACHED();
</del><ins>+ m_insertions.insert(0, insertion);
</ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> void InsertionSet::execute(BasicBlock* block)
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGLoopPreHeaderCreationPhasecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGLoopPreHeaderCreationPhase.cpp (188978 => 188979)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGLoopPreHeaderCreationPhase.cpp        2015-08-26 19:21:19 UTC (rev 188978)
+++ trunk/Source/JavaScriptCore/dfg/DFGLoopPreHeaderCreationPhase.cpp        2015-08-26 19:24:41 UTC (rev 188979)
</span><span class="lines">@@ -1,5 +1,5 @@
</span><span class="cx"> /*
</span><del>- * Copyright (C) 2013 Apple Inc. All rights reserved.
</del><ins>+ * Copyright (C) 2013, 2015 Apple Inc. All rights reserved.
</ins><span class="cx"> *
</span><span class="cx"> * Redistribution and use in source and binary forms, with or without
</span><span class="cx"> * modification, are permitted provided that the following conditions
</span><span class="lines">@@ -104,6 +104,13 @@
</span><span class="cx"> // because that is the DFG SSA convention. Therefore, each predecessor of the loop
</span><span class="cx"> // header must have only one successor.
</span><span class="cx"> DFG_ASSERT(m_graph, nullptr, existingPreHeader->terminal()->op() == Jump);
</span><ins>+
+ // A pre-header is most useful if it's possible to exit from its terminal. Hence
+ // if the terminal of the existing pre-header doesn't allow for exit, but the first
+ // origin of the loop header does, then we should create a new pre-header.
+ if (!needsNewPreHeader && loop.header()->firstOrigin().exitOK
+ && !existingPreHeader->terminal()->origin.exitOK)
+ needsNewPreHeader = true;
</ins><span class="cx">
</span><span class="cx"> if (!needsNewPreHeader)
</span><span class="cx"> continue;
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGMayExitcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGMayExit.cpp (188978 => 188979)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGMayExit.cpp        2015-08-26 19:21:19 UTC (rev 188978)
+++ trunk/Source/JavaScriptCore/dfg/DFGMayExit.cpp        2015-08-26 19:24:41 UTC (rev 188979)
</span><span class="lines">@@ -79,12 +79,14 @@
</span><span class="cx">
</span><span class="cx"> } // anonymous namespace
</span><span class="cx">
</span><del>-bool mayExit(Graph& graph, Node* node)
</del><ins>+ExitMode mayExit(Graph& graph, Node* node)
</ins><span class="cx"> {
</span><ins>+ ExitMode result = DoesNotExit;
+
</ins><span class="cx"> switch (node->op()) {
</span><span class="cx"> // This is a carefully curated list of nodes that definitely do not exit. We try to be very
</span><span class="cx"> // conservative when maintaining this list, because adding new node types to it doesn't
</span><del>- // generally make things a lot better but it might introduce insanely subtle bugs.
</del><ins>+ // generally make things a lot better but it might introduce subtle bugs.
</ins><span class="cx"> case SetArgument:
</span><span class="cx"> case JSConstant:
</span><span class="cx"> case DoubleConstant:
</span><span class="lines">@@ -94,11 +96,13 @@
</span><span class="cx"> case Flush:
</span><span class="cx"> case Phantom:
</span><span class="cx"> case Check:
</span><ins>+ case Identity:
</ins><span class="cx"> case GetLocal:
</span><span class="cx"> case LoopHint:
</span><span class="cx"> case Phi:
</span><span class="cx"> case Upsilon:
</span><span class="cx"> case ZombieHint:
</span><ins>+ case ExitOK:
</ins><span class="cx"> case BottomValue:
</span><span class="cx"> case PutHint:
</span><span class="cx"> case PhantomNewObject:
</span><span class="lines">@@ -116,18 +120,66 @@
</span><span class="cx"> case DoubleRep:
</span><span class="cx"> case Int52Rep:
</span><span class="cx"> case ValueRep:
</span><ins>+ case ExtractOSREntryLocal:
+ case LogicalNot:
+ case NotifyWrite:
+ case PutStructure:
+ case StoreBarrier:
+ case PutByOffset:
+ case PutClosureVar:
</ins><span class="cx"> break;
</span><del>-
</del><ins>+
+ case StrCat:
+ case Call:
+ case Construct:
+ case CallVarargs:
+ case ConstructVarargs:
+ case CallForwardVarargs:
+ case ConstructForwardVarargs:
+ case MaterializeCreateActivation:
+ case MaterializeNewObject:
+ case NewFunction:
+ case NewArrowFunction:
+ case NewStringObject:
+ case CreateActivation:
+ result = ExitsForExceptions;
+ break;
+
</ins><span class="cx"> default:
</span><span class="cx"> // If in doubt, return true.
</span><del>- return true;
</del><ins>+ return Exits;
</ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> EdgeMayExit functor;
</span><span class="cx"> DFG_NODE_DO_TO_CHILDREN(graph, node, functor);
</span><del>- return functor.result();
</del><ins>+ if (functor.result())
+ result = Exits;
+
+ return result;
</ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> } } // namespace JSC::DFG
</span><span class="cx">
</span><ins>+namespace WTF {
+
+using namespace JSC::DFG;
+
+void printInternal(PrintStream& out, ExitMode mode)
+{
+ switch (mode) {
+ case DoesNotExit:
+ out.print("DoesNotExit");
+ return;
+ case ExitsForExceptions:
+ out.print("ExitsForExceptions");
+ return;
+ case Exits:
+ out.print("Exits");
+ return;
+ }
+ RELEASE_ASSERT_NOT_REACHED();
+}
+
+} // namespace WTF
+
</ins><span class="cx"> #endif // ENABLE(DFG_JIT)
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGMayExith"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGMayExit.h (188978 => 188979)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGMayExit.h        2015-08-26 19:21:19 UTC (rev 188978)
+++ trunk/Source/JavaScriptCore/dfg/DFGMayExit.h        2015-08-26 19:24:41 UTC (rev 188979)
</span><span class="lines">@@ -1,5 +1,5 @@
</span><span class="cx"> /*
</span><del>- * Copyright (C) 2014 Apple Inc. All rights reserved.
</del><ins>+ * Copyright (C) 2014, 2015 Apple Inc. All rights reserved.
</ins><span class="cx"> *
</span><span class="cx"> * Redistribution and use in source and binary forms, with or without
</span><span class="cx"> * modification, are permitted provided that the following conditions
</span><span class="lines">@@ -36,10 +36,48 @@
</span><span class="cx"> // A *very* conservative approximation of whether or not a node could possibly exit. Usually
</span><span class="cx"> // returns true except in cases where we obviously don't expect an exit.
</span><span class="cx">
</span><del>-bool mayExit(Graph&, Node*);
</del><ins>+enum ExitMode {
+ // The node does not exit at all. This means that it's legal to eliminate the first store in a
+ // program like:
+ //
+ // global = 1 // first store
+ // DoesNotExit(); // let's assume that this also doesn't read "global"
+ // global = 2 // second store
+ //
+ // It's legal to eliminate the first one since nobody will see it; the second store is always
+ // executed right after.
+ DoesNotExit,
</ins><span class="cx">
</span><ins>+ // The node will exit, but only by properly throwing exceptions. A proper exception throw will
+ // divert execution to the matching op_catch and will not reexecute the exit origin. This means
+ // that the store elimination optimization above would be illegal, but the following would be OK:
+ //
+ // SideEffect(..., exit: bc#42)
+ // ExitsForExceptions(..., exit: #bc42, ExitInvalid)
+ //
+ // In particular, it's OK for a node that reports ExitsForExceptions to be executed in a context
+ // where !Node::origin.exitOK. That's because this node will not exit in a manner that could lead
+ // to the reexecution of SideEffect().
+ ExitsForExceptions,
+
+ // The node will exit to the exit origin. This means that we cannot do store elimination like for
+ // DoesNotExit and also we cannot place this node in an ExitInvalid context, since this will exit
+ // in a manner that will cause the reexecution of all previous operations within this exit origin.
+ Exits
+};
+
+ExitMode mayExit(Graph&, Node*);
+
</ins><span class="cx"> } } // namespace JSC::DFG
</span><span class="cx">
</span><ins>+namespace WTF {
+
+class PrintStream;
+
+void printInternal(PrintStream&, JSC::DFG::ExitMode);
+
+} // namespace WTF
+
</ins><span class="cx"> #endif // ENABLE(DFG_JIT)
</span><span class="cx">
</span><span class="cx"> #endif // DFGMayExit_h
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGMovHintRemovalPhasecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGMovHintRemovalPhase.cpp (188978 => 188979)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGMovHintRemovalPhase.cpp        2015-08-26 19:21:19 UTC (rev 188978)
+++ trunk/Source/JavaScriptCore/dfg/DFGMovHintRemovalPhase.cpp        2015-08-26 19:24:41 UTC (rev 188979)
</span><span class="lines">@@ -108,7 +108,7 @@
</span><span class="cx"> m_state.operand(node->unlinkedLocal()) = Epoch();
</span><span class="cx"> }
</span><span class="cx">
</span><del>- if (mayExit(m_graph, node))
</del><ins>+ if (mayExit(m_graph, node) != DoesNotExit)
</ins><span class="cx"> currentEpoch.bump();
</span><span class="cx">
</span><span class="cx"> if (nodeIndex) {
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGNodeOrigincpp"></a>
<div class="addfile"><h4>Added: trunk/Source/JavaScriptCore/dfg/DFGNodeOrigin.cpp (0 => 188979)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGNodeOrigin.cpp         (rev 0)
+++ trunk/Source/JavaScriptCore/dfg/DFGNodeOrigin.cpp        2015-08-26 19:24:41 UTC (rev 188979)
</span><span class="lines">@@ -0,0 +1,41 @@
</span><ins>+/*
+ * 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 "DFGNodeOrigin.h"
+
+#if ENABLE(DFG_JIT)
+
+namespace JSC { namespace DFG {
+
+void NodeOrigin::dump(PrintStream& out) const
+{
+ out.print("{semantic: ", semantic, ", forExit: ", forExit, ", exitOK: ", exitOK, "}");
+}
+
+} } // namespace JSC::DFG
+
+#endif // ENABLE(DFG_JIT)
+
</ins></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGNodeOriginh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGNodeOrigin.h (188978 => 188979)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGNodeOrigin.h        2015-08-26 19:21:19 UTC (rev 188978)
+++ trunk/Source/JavaScriptCore/dfg/DFGNodeOrigin.h        2015-08-26 19:24:41 UTC (rev 188979)
</span><span class="lines">@@ -1,5 +1,5 @@
</span><span class="cx"> /*
</span><del>- * Copyright (C) 2014 Apple Inc. All rights reserved.
</del><ins>+ * Copyright (C) 2014, 2015 Apple Inc. All rights reserved.
</ins><span class="cx"> *
</span><span class="cx"> * Redistribution and use in source and binary forms, with or without
</span><span class="cx"> * modification, are permitted provided that the following conditions
</span><span class="lines">@@ -29,9 +29,13 @@
</span><span class="cx"> #if ENABLE(DFG_JIT)
</span><span class="cx">
</span><span class="cx"> #include "CodeOrigin.h"
</span><ins>+#include "DFGClobbersExitState.h"
</ins><span class="cx">
</span><span class="cx"> namespace JSC { namespace DFG {
</span><span class="cx">
</span><ins>+class Graph;
+struct Node;
+
</ins><span class="cx"> struct NodeOrigin {
</span><span class="cx"> NodeOrigin() { }
</span><span class="cx">
</span><span class="lines">@@ -48,7 +52,7 @@
</span><span class="cx"> return semantic.isSet();
</span><span class="cx"> }
</span><span class="cx">
</span><del>- NodeOrigin withSemantic(CodeOrigin semantic)
</del><ins>+ NodeOrigin withSemantic(CodeOrigin semantic) const
</ins><span class="cx"> {
</span><span class="cx"> if (!isSet())
</span><span class="cx"> return NodeOrigin();
</span><span class="lines">@@ -58,7 +62,46 @@
</span><span class="cx"> result.semantic = semantic;
</span><span class="cx"> return result;
</span><span class="cx"> }
</span><ins>+
+ NodeOrigin withExitOK(bool value) const
+ {
+ NodeOrigin result = *this;
+ result.exitOK = value;
+ return result;
+ }
+
+ NodeOrigin withInvalidExit() const
+ {
+ return withExitOK(false);
+ }
+
+ NodeOrigin takeValidExit(bool& canExit) const
+ {
+ return withExitOK(exitOK & std::exchange(canExit, false));
+ }
</ins><span class="cx">
</span><ins>+ NodeOrigin forInsertingAfter(Graph& graph, Node* node) const
+ {
+ NodeOrigin result = *this;
+ if (exitOK && clobbersExitState(graph, node))
+ result.exitOK = false;
+ return result;
+ }
+
+ bool operator==(const NodeOrigin& other) const
+ {
+ return semantic == other.semantic
+ && forExit == other.forExit
+ && exitOK == other.exitOK;
+ }
+
+ bool operator!=(const NodeOrigin& other) const
+ {
+ return !(*this == other);
+ }
+
+ void dump(PrintStream&) const;
+
</ins><span class="cx"> // Used for determining what bytecode this came from. This is important for
</span><span class="cx"> // debugging, exceptions, and even basic execution semantics.
</span><span class="cx"> CodeOrigin semantic;
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGNodeTypeh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGNodeType.h (188978 => 188979)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGNodeType.h        2015-08-26 19:21:19 UTC (rev 188978)
+++ trunk/Source/JavaScriptCore/dfg/DFGNodeType.h        2015-08-26 19:24:41 UTC (rev 188979)
</span><span class="lines">@@ -71,6 +71,7 @@
</span><span class="cx"> \
</span><span class="cx"> macro(MovHint, NodeMustGenerate) \
</span><span class="cx"> macro(ZombieHint, NodeMustGenerate) \
</span><ins>+ macro(ExitOK, NodeMustGenerate) /* Indicates that exit state is intact. */ \
</ins><span class="cx"> macro(Phantom, NodeMustGenerate) \
</span><span class="cx"> macro(Check, NodeMustGenerate) /* Used if we want just a type check but not liveness. Non-checking uses will be removed. */\
</span><span class="cx"> macro(Upsilon, 0) \
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGOSREntrypointCreationPhasecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGOSREntrypointCreationPhase.cpp (188978 => 188979)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGOSREntrypointCreationPhase.cpp        2015-08-26 19:21:19 UTC (rev 188978)
+++ trunk/Source/JavaScriptCore/dfg/DFGOSREntrypointCreationPhase.cpp        2015-08-26 19:24:41 UTC (rev 188979)
</span><span class="lines">@@ -1,5 +1,5 @@
</span><span class="cx"> /*
</span><del>- * Copyright (C) 2013, 2014 Apple Inc. All rights reserved.
</del><ins>+ * Copyright (C) 2013-2015 Apple Inc. All rights reserved.
</ins><span class="cx"> *
</span><span class="cx"> * Redistribution and use in source and binary forms, with or without
</span><span class="cx"> * modification, are permitted provided that the following conditions
</span><span class="lines">@@ -89,7 +89,9 @@
</span><span class="cx"> // executionCount to use best judgement - but that seems unnecessary since we know for
</span><span class="cx"> // sure what the executionCount should be in this case.
</span><span class="cx"> BasicBlock* newRoot = insertionSet.insert(0, 1);
</span><del>- NodeOrigin origin = target->at(0)->origin;
</del><ins>+
+ // We'd really like to use an unset origin, but ThreadedCPS won't allow that.
+ NodeOrigin origin = NodeOrigin(CodeOrigin(0), CodeOrigin(0), false);
</ins><span class="cx">
</span><span class="cx"> Vector<Node*> locals(baseline->m_numCalleeRegisters);
</span><span class="cx"> for (int local = 0; local < baseline->m_numCalleeRegisters; ++local) {
</span><span class="lines">@@ -106,6 +108,10 @@
</span><span class="cx"> Edge(locals[local]));
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+ // Now use the origin of the target, since it's not OK to exit, and we will probably hoist
+ // type checks to here.
+ origin = target->at(0)->origin;
+
</ins><span class="cx"> for (int argument = 0; argument < baseline->numParameters(); ++argument) {
</span><span class="cx"> Node* oldNode = target->variablesAtHead.argument(argument);
</span><span class="cx"> if (!oldNode) {
</span><span class="lines">@@ -117,7 +123,7 @@
</span><span class="cx"> OpInfo(oldNode->variableAccessData()));
</span><span class="cx"> m_graph.m_arguments[argument] = node;
</span><span class="cx"> }
</span><del>-
</del><ins>+
</ins><span class="cx"> for (int local = 0; local < baseline->m_numCalleeRegisters; ++local) {
</span><span class="cx"> Node* previousHead = target->variablesAtHead.local(local);
</span><span class="cx"> if (!previousHead)
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGOSRExitcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGOSRExit.cpp (188978 => 188979)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGOSRExit.cpp        2015-08-26 19:21:19 UTC (rev 188978)
+++ trunk/Source/JavaScriptCore/dfg/DFGOSRExit.cpp        2015-08-26 19:24:41 UTC (rev 188979)
</span><span class="lines">@@ -36,14 +36,14 @@
</span><span class="cx"> namespace JSC { namespace DFG {
</span><span class="cx">
</span><span class="cx"> OSRExit::OSRExit(ExitKind kind, JSValueSource jsValueSource, MethodOfGettingAValueProfile valueProfile, SpeculativeJIT* jit, unsigned streamIndex, unsigned recoveryIndex)
</span><del>- : OSRExitBase(kind, jit->m_codeOriginForExitTarget, jit->m_codeOriginForExitProfile)
</del><ins>+ : OSRExitBase(kind, jit->m_origin.forExit, jit->m_origin.semantic)
</ins><span class="cx"> , m_jsValueSource(jsValueSource)
</span><span class="cx"> , m_valueProfile(valueProfile)
</span><span class="cx"> , m_patchableCodeOffset(0)
</span><span class="cx"> , m_recoveryIndex(recoveryIndex)
</span><span class="cx"> , m_streamIndex(streamIndex)
</span><span class="cx"> {
</span><del>- ASSERT(m_codeOrigin.isSet());
</del><ins>+ DFG_ASSERT(jit->m_jit.graph(), jit->m_currentNode, jit->m_origin.exitOK);
</ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> void OSRExit::setPatchableCodeOffset(MacroAssembler::PatchableJump check)
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGOSRExitBaseh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGOSRExitBase.h (188978 => 188979)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGOSRExitBase.h        2015-08-26 19:21:19 UTC (rev 188978)
+++ trunk/Source/JavaScriptCore/dfg/DFGOSRExitBase.h        2015-08-26 19:24:41 UTC (rev 188979)
</span><span class="lines">@@ -1,5 +1,5 @@
</span><span class="cx"> /*
</span><del>- * Copyright (C) 2013 Apple Inc. All rights reserved.
</del><ins>+ * Copyright (C) 2013, 2015 Apple Inc. All rights reserved.
</ins><span class="cx"> *
</span><span class="cx"> * Redistribution and use in source and binary forms, with or without
</span><span class="cx"> * modification, are permitted provided that the following conditions
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGObjectAllocationSinkingPhasecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGObjectAllocationSinkingPhase.cpp (188978 => 188979)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGObjectAllocationSinkingPhase.cpp        2015-08-26 19:21:19 UTC (rev 188978)
+++ trunk/Source/JavaScriptCore/dfg/DFGObjectAllocationSinkingPhase.cpp        2015-08-26 19:24:41 UTC (rev 188979)
</span><span class="lines">@@ -29,6 +29,7 @@
</span><span class="cx"> #if ENABLE(DFG_JIT)
</span><span class="cx">
</span><span class="cx"> #include "DFGBlockMapInlines.h"
</span><ins>+#include "DFGClobbersExitState.h"
</ins><span class="cx"> #include "DFGCombinedLiveness.h"
</span><span class="cx"> #include "DFGGraph.h"
</span><span class="cx"> #include "DFGInsertionSet.h"
</span><span class="lines">@@ -757,7 +758,7 @@
</span><span class="cx"> promoteLocalHeap();
</span><span class="cx">
</span><span class="cx"> if (Options::validateGraphAtEachPhase())
</span><del>- validate(m_graph, DumpGraph, graphBeforeSinking);
</del><ins>+ DFG::validate(m_graph, DumpGraph, graphBeforeSinking);
</ins><span class="cx"> return true;
</span><span class="cx"> }
</span><span class="cx">
</span><span class="lines">@@ -1678,7 +1679,8 @@
</span><span class="cx">
</span><span class="cx"> Node* identifier = indexToNode[variable->index()];
</span><span class="cx"> m_escapeeToMaterialization.add(identifier, phiDef->value());
</span><del>- insertOSRHintsForUpdate(0, NodeOrigin(), availabilityCalculator.m_availability, identifier, phiDef->value());
</del><ins>+ bool canExit = false;
+ insertOSRHintsForUpdate(0, NodeOrigin(), canExit, availabilityCalculator.m_availability, identifier, phiDef->value());
</ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> if (verbose) {
</span><span class="lines">@@ -1688,6 +1690,8 @@
</span><span class="cx">
</span><span class="cx"> for (unsigned nodeIndex = 0; nodeIndex < block->size(); ++nodeIndex) {
</span><span class="cx"> Node* node = block->at(nodeIndex);
</span><ins>+ bool canExit = true;
+ bool nextCanExit = node->origin.exitOK;
</ins><span class="cx"> for (PromotedHeapLocation location : m_locationsForAllocation.get(node)) {
</span><span class="cx"> if (location.kind() != NamedPropertyPLoc)
</span><span class="cx"> continue;
</span><span class="lines">@@ -1697,11 +1701,13 @@
</span><span class="cx"> if (m_sinkCandidates.contains(node)) {
</span><span class="cx"> m_insertionSet.insert(
</span><span class="cx"> nodeIndex + 1,
</span><del>- location.createHint(m_graph, node->origin, m_bottom));
</del><ins>+ location.createHint(
+ m_graph, node->origin.takeValidExit(nextCanExit), m_bottom));
</ins><span class="cx"> }
</span><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> for (Node* materialization : m_materializationSiteToMaterializations.get(node)) {
</span><ins>+ materialization->origin.exitOK &= canExit;
</ins><span class="cx"> Node* escapee = m_materializationToEscapee.get(materialization);
</span><span class="cx"> populateMaterialization(block, materialization, escapee);
</span><span class="cx"> m_escapeeToMaterialization.set(escapee, materialization);
</span><span class="lines">@@ -1711,7 +1717,7 @@
</span><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> for (PromotedHeapLocation location : m_materializationSiteToRecoveries.get(node))
</span><del>- m_insertionSet.insert(nodeIndex, createRecovery(block, location, node));
</del><ins>+ m_insertionSet.insert(nodeIndex, createRecovery(block, location, node, canExit));
</ins><span class="cx">
</span><span class="cx"> // We need to put the OSR hints after the recoveries,
</span><span class="cx"> // because we only want the hints once the object is
</span><span class="lines">@@ -1719,15 +1725,24 @@
</span><span class="cx"> for (Node* materialization : m_materializationSiteToMaterializations.get(node)) {
</span><span class="cx"> Node* escapee = m_materializationToEscapee.get(materialization);
</span><span class="cx"> insertOSRHintsForUpdate(
</span><del>- nodeIndex, node->origin,
</del><ins>+ nodeIndex, node->origin, canExit,
</ins><span class="cx"> availabilityCalculator.m_availability, escapee, materialization);
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+ if (node->origin.exitOK && !canExit) {
+ // We indicate that the exit state is fine now. It is OK because we updated the
+ // state above. We need to indicate this manually because the validation doesn't
+ // have enough information to infer that the exit state is fine.
+ m_insertionSet.insertNode(nodeIndex, SpecNone, ExitOK, node->origin);
+ }
+
</ins><span class="cx"> if (m_sinkCandidates.contains(node))
</span><span class="cx"> m_escapeeToMaterialization.set(node, node);
</span><span class="cx">
</span><span class="cx"> availabilityCalculator.executeNode(node);
</span><span class="cx">
</span><ins>+ bool desiredNextExitOK = node->origin.exitOK && !clobbersExitState(m_graph, node);
+
</ins><span class="cx"> bool doLower = false;
</span><span class="cx"> handleNode(
</span><span class="cx"> node,
</span><span class="lines">@@ -1750,13 +1765,21 @@
</span><span class="cx">
</span><span class="cx"> doLower = true;
</span><span class="cx">
</span><del>- m_insertionSet.insert(nodeIndex + 1,
- location.createHint(m_graph, node->origin, nodeValue));
</del><ins>+ m_insertionSet.insert(
+ nodeIndex + 1,
+ location.createHint(
+ m_graph, node->origin.takeValidExit(nextCanExit), nodeValue));
</ins><span class="cx"> },
</span><span class="cx"> [&] (PromotedHeapLocation location) -> Node* {
</span><span class="cx"> return resolve(block, location);
</span><span class="cx"> });
</span><span class="cx">
</span><ins>+ if (!nextCanExit && desiredNextExitOK) {
+ // We indicate that the exit state is fine now. We need to do this because we
+ // emitted hints that appear to invalidate the exit state.
+ m_insertionSet.insertNode(nodeIndex + 1, SpecNone, ExitOK, node->origin);
+ }
+
</ins><span class="cx"> if (m_sinkCandidates.contains(node) || doLower) {
</span><span class="cx"> switch (node->op()) {
</span><span class="cx"> case NewObject:
</span><span class="lines">@@ -1871,7 +1894,7 @@
</span><span class="cx"> return def->value();
</span><span class="cx"> }
</span><span class="cx">
</span><del>- void insertOSRHintsForUpdate(unsigned nodeIndex, NodeOrigin origin, AvailabilityMap& availability, Node* escapee, Node* materialization)
</del><ins>+ void insertOSRHintsForUpdate(unsigned nodeIndex, NodeOrigin origin, bool& canExit, AvailabilityMap& availability, Node* escapee, Node* materialization)
</ins><span class="cx"> {
</span><span class="cx"> // We need to follow() the value in the heap.
</span><span class="cx"> // Consider the following graph:
</span><span class="lines">@@ -1905,7 +1928,8 @@
</span><span class="cx"> continue;
</span><span class="cx">
</span><span class="cx"> m_insertionSet.insert(
</span><del>- nodeIndex, entry.key.createHint(m_graph, origin, materialization));
</del><ins>+ nodeIndex,
+ entry.key.createHint(m_graph, origin.takeValidExit(canExit), materialization));
</ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> for (unsigned i = availability.m_locals.size(); i--;) {
</span><span class="lines">@@ -1916,7 +1940,7 @@
</span><span class="cx">
</span><span class="cx"> int operand = availability.m_locals.operandForIndex(i);
</span><span class="cx"> m_insertionSet.insertNode(
</span><del>- nodeIndex, SpecNone, MovHint, origin, OpInfo(operand),
</del><ins>+ nodeIndex, SpecNone, MovHint, origin.takeValidExit(canExit), OpInfo(operand),
</ins><span class="cx"> materialization->defaultEdge());
</span><span class="cx"> }
</span><span class="cx"> }
</span><span class="lines">@@ -2042,7 +2066,7 @@
</span><span class="cx"> }
</span><span class="cx"> }
</span><span class="cx">
</span><del>- Node* createRecovery(BasicBlock* block, PromotedHeapLocation location, Node* where)
</del><ins>+ Node* createRecovery(BasicBlock* block, PromotedHeapLocation location, Node* where, bool& canExit)
</ins><span class="cx"> {
</span><span class="cx"> if (verbose)
</span><span class="cx"> dataLog("Recovering ", location, " at ", where, "\n");
</span><span class="lines">@@ -2050,12 +2074,14 @@
</span><span class="cx"> Node* base = getMaterialization(block, location.base());
</span><span class="cx"> Node* value = resolve(block, location);
</span><span class="cx">
</span><ins>+ NodeOrigin origin = where->origin.withSemantic(base->origin.semantic);
+
</ins><span class="cx"> if (verbose)
</span><span class="cx"> dataLog("Base is ", base, " and value is ", value, "\n");
</span><span class="cx">
</span><span class="cx"> if (base->isPhantomAllocation()) {
</span><span class="cx"> return PromotedHeapLocation(base, location.descriptor()).createHint(
</span><del>- m_graph, where->origin.withSemantic(base->origin.semantic), value);
</del><ins>+ m_graph, origin.takeValidExit(canExit), value);
</ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> switch (location.kind()) {
</span><span class="lines">@@ -2089,7 +2115,7 @@
</span><span class="cx"> return m_graph.addNode(
</span><span class="cx"> SpecNone,
</span><span class="cx"> PutByOffset,
</span><del>- where->origin,
</del><ins>+ origin.takeValidExit(canExit),
</ins><span class="cx"> OpInfo(data),
</span><span class="cx"> Edge(storage, KnownCellUse),
</span><span class="cx"> Edge(base, KnownCellUse),
</span><span class="lines">@@ -2118,7 +2144,7 @@
</span><span class="cx"> return m_graph.addNode(
</span><span class="cx"> SpecNone,
</span><span class="cx"> MultiPutByOffset,
</span><del>- where->origin.withSemantic(base->origin.semantic),
</del><ins>+ origin.takeValidExit(canExit),
</ins><span class="cx"> OpInfo(data),
</span><span class="cx"> Edge(base, KnownCellUse),
</span><span class="cx"> value->defaultEdge());
</span><span class="lines">@@ -2129,7 +2155,7 @@
</span><span class="cx"> return m_graph.addNode(
</span><span class="cx"> SpecNone,
</span><span class="cx"> PutClosureVar,
</span><del>- where->origin.withSemantic(base->origin.semantic),
</del><ins>+ origin.takeValidExit(canExit),
</ins><span class="cx"> OpInfo(location.info()),
</span><span class="cx"> Edge(base, KnownCellUse),
</span><span class="cx"> value->defaultEdge());
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGPhantomInsertionPhasecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGPhantomInsertionPhase.cpp (188978 => 188979)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGPhantomInsertionPhase.cpp        2015-08-26 19:21:19 UTC (rev 188978)
+++ trunk/Source/JavaScriptCore/dfg/DFGPhantomInsertionPhase.cpp        2015-08-26 19:24:41 UTC (rev 188979)
</span><span class="lines">@@ -123,7 +123,7 @@
</span><span class="cx"> break;
</span><span class="cx"> }
</span><span class="cx">
</span><del>- if (mayExit(m_graph, node)) {
</del><ins>+ if (mayExit(m_graph, node) != DoesNotExit) {
</ins><span class="cx"> currentEpoch.bump();
</span><span class="cx"> lastExitingIndex = nodeIndex;
</span><span class="cx"> }
</span><span class="lines">@@ -160,10 +160,13 @@
</span><span class="cx"> // We have exact ref counts, so creating a new use means that we have to
</span><span class="cx"> // increment the ref count.
</span><span class="cx"> killedNode->postfixRef();
</span><ins>+
+ Node* lastExitingNode = block->at(lastExitingIndex);
</ins><span class="cx">
</span><span class="cx"> m_insertionSet.insertNode(
</span><span class="cx"> lastExitingIndex + 1, SpecNone, Phantom,
</span><del>- block->at(lastExitingIndex)->origin, killedNode->defaultEdge());
</del><ins>+ lastExitingNode->origin.forInsertingAfter(m_graph, lastExitingNode),
+ killedNode->defaultEdge());
</ins><span class="cx"> });
</span><span class="cx"> }
</span><span class="cx">
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGPhasecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGPhase.cpp (188978 => 188979)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGPhase.cpp        2015-08-26 19:21:19 UTC (rev 188978)
+++ trunk/Source/JavaScriptCore/dfg/DFGPhase.cpp        2015-08-26 19:24:41 UTC (rev 188979)
</span><span class="lines">@@ -33,6 +33,11 @@
</span><span class="cx">
</span><span class="cx"> namespace JSC { namespace DFG {
</span><span class="cx">
</span><ins>+void Phase::validate()
+{
+ DFG::validate(m_graph, DumpGraph, m_graphDumpBeforePhase);
+}
+
</ins><span class="cx"> void Phase::beginPhase()
</span><span class="cx"> {
</span><span class="cx"> if (Options::verboseValidationFailure()) {
</span><span class="lines">@@ -53,7 +58,7 @@
</span><span class="cx"> {
</span><span class="cx"> if (!Options::validateGraphAtEachPhase())
</span><span class="cx"> return;
</span><del>- validate(m_graph, DumpGraph, m_graphDumpBeforePhase);
</del><ins>+ validate();
</ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> } } // namespace JSC::DFG
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGPhaseh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGPhase.h (188978 => 188979)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGPhase.h        2015-08-26 19:21:19 UTC (rev 188978)
+++ trunk/Source/JavaScriptCore/dfg/DFGPhase.h        2015-08-26 19:24:41 UTC (rev 188979)
</span><span class="lines">@@ -60,6 +60,9 @@
</span><span class="cx"> VM& vm() { return m_graph.m_vm; }
</span><span class="cx"> CodeBlock* codeBlock() { return m_graph.m_codeBlock; }
</span><span class="cx"> CodeBlock* profiledBlock() { return m_graph.m_profiledBlock; }
</span><ins>+
+ // This runs validation, and uses the graph dump before the phase if possible.
+ void validate();
</ins><span class="cx">
</span><span class="cx"> const char* m_name;
</span><span class="cx">
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGPredictionPropagationPhasecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp (188978 => 188979)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp        2015-08-26 19:21:19 UTC (rev 188978)
+++ trunk/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp        2015-08-26 19:24:41 UTC (rev 188979)
</span><span class="lines">@@ -668,6 +668,7 @@
</span><span class="cx"> case ConstantStoragePointer:
</span><span class="cx"> case MovHint:
</span><span class="cx"> case ZombieHint:
</span><ins>+ case ExitOK:
</ins><span class="cx"> case LoadVarargs:
</span><span class="cx"> break;
</span><span class="cx">
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGPutStackSinkingPhasecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGPutStackSinkingPhase.cpp (188978 => 188979)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGPutStackSinkingPhase.cpp        2015-08-26 19:21:19 UTC (rev 188978)
+++ trunk/Source/JavaScriptCore/dfg/DFGPutStackSinkingPhase.cpp        2015-08-26 19:24:41 UTC (rev 188979)
</span><span class="lines">@@ -461,7 +461,7 @@
</span><span class="cx"> insertionSet.insertNode(
</span><span class="cx"> nodeIndex, SpecNone, PutStack, node->origin,
</span><span class="cx"> OpInfo(m_graph.m_stackAccessData.add(operand, format)),
</span><del>- Edge(incoming, useKindFor(format)));
</del><ins>+ Edge(incoming, uncheckedUseKindFor(format)));
</ins><span class="cx">
</span><span class="cx"> deferred.operand(operand) = DeadFlush;
</span><span class="cx"> };
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGSSAConversionPhasecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGSSAConversionPhase.cpp (188978 => 188979)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGSSAConversionPhase.cpp        2015-08-26 19:21:19 UTC (rev 188978)
+++ trunk/Source/JavaScriptCore/dfg/DFGSSAConversionPhase.cpp        2015-08-26 19:24:41 UTC (rev 188979)
</span><span class="lines">@@ -271,6 +271,7 @@
</span><span class="cx"> m_insertionSet.insertNode(
</span><span class="cx"> nodeIndex, SpecNone, KillStack, node->origin,
</span><span class="cx"> OpInfo(node->unlinkedLocal().offset()));
</span><ins>+ node->origin.exitOK = false; // KillStack clobbers exit.
</ins><span class="cx"> break;
</span><span class="cx"> }
</span><span class="cx">
</span><span class="lines">@@ -346,7 +347,11 @@
</span><span class="cx"> SSACalculator::Variable* ssaVariable = phiDef->variable();
</span><span class="cx"> VariableAccessData* variable = m_variableForSSAIndex[ssaVariable->index()];
</span><span class="cx"> FlushFormat format = variable->flushFormat();
</span><del>- UseKind useKind = useKindFor(format);
</del><ins>+
+ // We can use an unchecked use kind because the SetLocal was turned into a Check.
+ // We have to use an unchecked use because at least sometimes, the end of the block
+ // is not exitOK.
+ UseKind useKind = uncheckedUseKindFor(format);
</ins><span class="cx">
</span><span class="cx"> m_insertionSet.insertNode(
</span><span class="cx"> upsilonInsertionPoint, SpecNone, Upsilon, upsilonOrigin,
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGSafeToExecuteh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGSafeToExecute.h (188978 => 188979)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGSafeToExecute.h        2015-08-26 19:21:19 UTC (rev 188978)
+++ trunk/Source/JavaScriptCore/dfg/DFGSafeToExecute.h        2015-08-26 19:24:41 UTC (rev 188979)
</span><span class="lines">@@ -138,6 +138,7 @@
</span><span class="cx"> case GetStack:
</span><span class="cx"> case MovHint:
</span><span class="cx"> case ZombieHint:
</span><ins>+ case ExitOK:
</ins><span class="cx"> case Phantom:
</span><span class="cx"> case Upsilon:
</span><span class="cx"> case Phi:
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGSpeculativeJITcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp (188978 => 188979)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp        2015-08-26 19:21:19 UTC (rev 188978)
+++ trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp        2015-08-26 19:24:41 UTC (rev 188979)
</span><span class="lines">@@ -61,7 +61,6 @@
</span><span class="cx"> , m_interpreter(m_jit.graph(), m_state)
</span><span class="cx"> , m_stream(&jit.jitCode()->variableEventStream)
</span><span class="cx"> , m_minifiedGraph(&jit.jitCode()->minifiedDFG)
</span><del>- , m_isCheckingArgumentTypes(false)
</del><span class="cx"> {
</span><span class="cx"> }
</span><span class="cx">
</span><span class="lines">@@ -196,7 +195,6 @@
</span><span class="cx"> {
</span><span class="cx"> if (!m_compileOkay)
</span><span class="cx"> return;
</span><del>- ASSERT(m_isCheckingArgumentTypes || m_canExit);
</del><span class="cx"> JITCompiler::Jump fuzzJump = emitOSRExitFuzzCheck();
</span><span class="cx"> if (fuzzJump.isSet()) {
</span><span class="cx"> JITCompiler::JumpList jumpsToFail;
</span><span class="lines">@@ -212,7 +210,6 @@
</span><span class="cx"> {
</span><span class="cx"> if (!m_compileOkay)
</span><span class="cx"> return;
</span><del>- ASSERT(m_isCheckingArgumentTypes || m_canExit);
</del><span class="cx"> JITCompiler::Jump fuzzJump = emitOSRExitFuzzCheck();
</span><span class="cx"> if (fuzzJump.isSet()) {
</span><span class="cx"> JITCompiler::JumpList myJumpsToFail;
</span><span class="lines">@@ -228,7 +225,6 @@
</span><span class="cx"> {
</span><span class="cx"> if (!m_compileOkay)
</span><span class="cx"> return OSRExitJumpPlaceholder();
</span><del>- ASSERT(m_isCheckingArgumentTypes || m_canExit);
</del><span class="cx"> unsigned index = m_jit.jitCode()->osrExit.size();
</span><span class="cx"> m_jit.appendExitInfo();
</span><span class="cx"> m_jit.jitCode()->appendOSRExit(OSRExit(kind, jsValueSource, m_jit.graph().methodOfGettingAValueProfileFor(node), this, m_stream->size()));
</span><span class="lines">@@ -237,19 +233,16 @@
</span><span class="cx">
</span><span class="cx"> OSRExitJumpPlaceholder SpeculativeJIT::speculationCheck(ExitKind kind, JSValueSource jsValueSource, Edge nodeUse)
</span><span class="cx"> {
</span><del>- ASSERT(m_isCheckingArgumentTypes || m_canExit);
</del><span class="cx"> return speculationCheck(kind, jsValueSource, nodeUse.node());
</span><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> void SpeculativeJIT::speculationCheck(ExitKind kind, JSValueSource jsValueSource, Edge nodeUse, MacroAssembler::Jump jumpToFail)
</span><span class="cx"> {
</span><del>- ASSERT(m_isCheckingArgumentTypes || m_canExit);
</del><span class="cx"> speculationCheck(kind, jsValueSource, nodeUse.node(), jumpToFail);
</span><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> void SpeculativeJIT::speculationCheck(ExitKind kind, JSValueSource jsValueSource, Edge nodeUse, const MacroAssembler::JumpList& jumpsToFail)
</span><span class="cx"> {
</span><del>- ASSERT(m_isCheckingArgumentTypes || m_canExit);
</del><span class="cx"> speculationCheck(kind, jsValueSource, nodeUse.node(), jumpsToFail);
</span><span class="cx"> }
</span><span class="cx">
</span><span class="lines">@@ -257,7 +250,6 @@
</span><span class="cx"> {
</span><span class="cx"> if (!m_compileOkay)
</span><span class="cx"> return;
</span><del>- ASSERT(m_isCheckingArgumentTypes || m_canExit);
</del><span class="cx"> unsigned recoveryIndex = m_jit.jitCode()->appendSpeculationRecovery(recovery);
</span><span class="cx"> m_jit.appendExitInfo(jumpToFail);
</span><span class="cx"> m_jit.jitCode()->appendOSRExit(OSRExit(kind, jsValueSource, m_jit.graph().methodOfGettingAValueProfileFor(node), this, m_stream->size(), recoveryIndex));
</span><span class="lines">@@ -265,7 +257,6 @@
</span><span class="cx">
</span><span class="cx"> void SpeculativeJIT::speculationCheck(ExitKind kind, JSValueSource jsValueSource, Edge nodeUse, MacroAssembler::Jump jumpToFail, const SpeculationRecovery& recovery)
</span><span class="cx"> {
</span><del>- ASSERT(m_isCheckingArgumentTypes || m_canExit);
</del><span class="cx"> speculationCheck(kind, jsValueSource, nodeUse.node(), jumpToFail, recovery);
</span><span class="cx"> }
</span><span class="cx">
</span><span class="lines">@@ -273,7 +264,6 @@
</span><span class="cx"> {
</span><span class="cx"> if (!m_compileOkay)
</span><span class="cx"> return;
</span><del>- ASSERT(m_canExit);
</del><span class="cx"> OSRExitCompilationInfo& info = m_jit.appendExitInfo(JITCompiler::JumpList());
</span><span class="cx"> m_jit.jitCode()->appendOSRExit(OSRExit(
</span><span class="cx"> UncountableInvalidation, JSValueSource(),
</span><span class="lines">@@ -286,7 +276,6 @@
</span><span class="cx">
</span><span class="cx"> void SpeculativeJIT::terminateSpeculativeExecution(ExitKind kind, JSValueRegs jsValueRegs, Node* node)
</span><span class="cx"> {
</span><del>- ASSERT(m_isCheckingArgumentTypes || m_canExit);
</del><span class="cx"> if (!m_compileOkay)
</span><span class="cx"> return;
</span><span class="cx"> speculationCheck(kind, jsValueRegs, node, m_jit.jump());
</span><span class="lines">@@ -297,7 +286,6 @@
</span><span class="cx">
</span><span class="cx"> void SpeculativeJIT::terminateSpeculativeExecution(ExitKind kind, JSValueRegs jsValueRegs, Edge nodeUse)
</span><span class="cx"> {
</span><del>- ASSERT(m_isCheckingArgumentTypes || m_canExit);
</del><span class="cx"> terminateSpeculativeExecution(kind, jsValueRegs, nodeUse.node());
</span><span class="cx"> }
</span><span class="cx">
</span><span class="lines">@@ -1468,10 +1456,9 @@
</span><span class="cx"> variable->machineLocal(),
</span><span class="cx"> format));
</span><span class="cx"> }
</span><ins>+
+ m_origin = NodeOrigin();
</ins><span class="cx">
</span><del>- m_codeOriginForExitTarget = CodeOrigin();
- m_codeOriginForExitProfile = CodeOrigin();
-
</del><span class="cx"> for (m_indexInBlock = 0; m_indexInBlock < m_block->size(); ++m_indexInBlock) {
</span><span class="cx"> m_currentNode = m_block->at(m_indexInBlock);
</span><span class="cx">
</span><span class="lines">@@ -1482,15 +1469,11 @@
</span><span class="cx"> return;
</span><span class="cx"> }
</span><span class="cx">
</span><del>- if (ASSERT_DISABLED)
- m_canExit = true; // Essentially disable the assertions.
- else
- m_canExit = mayExit(m_jit.graph(), m_currentNode);
-
</del><span class="cx"> m_interpreter.startExecuting();
</span><span class="cx"> m_jit.setForNode(m_currentNode);
</span><del>- m_codeOriginForExitTarget = m_currentNode->origin.forExit;
- m_codeOriginForExitProfile = m_currentNode->origin.semantic;
</del><ins>+ m_origin = m_currentNode->origin;
+ if (validationEnabled())
+ m_origin.exitOK &= mayExit(m_jit.graph(), m_currentNode) == Exits;
</ins><span class="cx"> m_lastGeneratedNode = m_currentNode->op();
</span><span class="cx">
</span><span class="cx"> ASSERT(m_currentNode->shouldGenerate());
</span><span class="lines">@@ -1537,9 +1520,7 @@
</span><span class="cx"> void SpeculativeJIT::checkArgumentTypes()
</span><span class="cx"> {
</span><span class="cx"> ASSERT(!m_currentNode);
</span><del>- m_isCheckingArgumentTypes = true;
- m_codeOriginForExitTarget = CodeOrigin(0);
- m_codeOriginForExitProfile = CodeOrigin(0);
</del><ins>+ m_origin = NodeOrigin(CodeOrigin(0), CodeOrigin(0), true);
</ins><span class="cx">
</span><span class="cx"> for (int i = 0; i < m_jit.codeBlock()->numParameters(); ++i) {
</span><span class="cx"> Node* node = m_jit.graph().m_arguments[i];
</span><span class="lines">@@ -1602,7 +1583,8 @@
</span><span class="cx"> }
</span><span class="cx"> #endif
</span><span class="cx"> }
</span><del>- m_isCheckingArgumentTypes = false;
</del><ins>+
+ m_origin = NodeOrigin();
</ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> bool SpeculativeJIT::compile()
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGSpeculativeJITh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h (188978 => 188979)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h        2015-08-26 19:21:19 UTC (rev 188978)
+++ trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h        2015-08-26 19:24:41 UTC (rev 188979)
</span><span class="lines">@@ -2458,7 +2458,6 @@
</span><span class="cx"> BasicBlock* m_block;
</span><span class="cx"> Node* m_currentNode;
</span><span class="cx"> NodeType m_lastGeneratedNode;
</span><del>- bool m_canExit;
</del><span class="cx"> unsigned m_indexInBlock;
</span><span class="cx"> // Virtual and physical register maps.
</span><span class="cx"> Vector<GenerationInfo, 32> m_generationInfo;
</span><span class="lines">@@ -2479,8 +2478,7 @@
</span><span class="cx"> };
</span><span class="cx"> Vector<BranchRecord, 8> m_branches;
</span><span class="cx">
</span><del>- CodeOrigin m_codeOriginForExitTarget;
- CodeOrigin m_codeOriginForExitProfile;
</del><ins>+ NodeOrigin m_origin;
</ins><span class="cx">
</span><span class="cx"> InPlaceAbstractState m_state;
</span><span class="cx"> AbstractInterpreter<InPlaceAbstractState> m_interpreter;
</span><span class="lines">@@ -2488,8 +2486,6 @@
</span><span class="cx"> VariableEventStream* m_stream;
</span><span class="cx"> MinifiedGraph* m_minifiedGraph;
</span><span class="cx">
</span><del>- bool m_isCheckingArgumentTypes;
-
</del><span class="cx"> Vector<std::unique_ptr<SlowPathGenerator>, 8> m_slowPathGenerators;
</span><span class="cx"> Vector<SilentRegisterSavePlan> m_plans;
</span><span class="cx"> };
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGSpeculativeJIT32_64cpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp (188978 => 188979)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp        2015-08-26 19:21:19 UTC (rev 188978)
+++ trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp        2015-08-26 19:24:41 UTC (rev 188979)
</span><span class="lines">@@ -1892,6 +1892,11 @@
</span><span class="cx"> noResult(node);
</span><span class="cx"> break;
</span><span class="cx"> }
</span><ins>+
+ case ExitOK: {
+ noResult(node);
+ break;
+ }
</ins><span class="cx">
</span><span class="cx"> case SetLocal: {
</span><span class="cx"> switch (node->variableAccessData()->flushFormat()) {
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGSpeculativeJIT64cpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp (188978 => 188979)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp        2015-08-26 19:21:19 UTC (rev 188978)
+++ trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp        2015-08-26 19:24:41 UTC (rev 188979)
</span><span class="lines">@@ -1971,6 +1971,11 @@
</span><span class="cx"> break;
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+ case ExitOK: {
+ noResult(node);
+ break;
+ }
+
</ins><span class="cx"> case SetLocal: {
</span><span class="cx"> switch (node->variableAccessData()->flushFormat()) {
</span><span class="cx"> case FlushedDouble: {
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGStoreBarrierInsertionPhasecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGStoreBarrierInsertionPhase.cpp (188978 => 188979)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGStoreBarrierInsertionPhase.cpp        2015-08-26 19:21:19 UTC (rev 188978)
+++ trunk/Source/JavaScriptCore/dfg/DFGStoreBarrierInsertionPhase.cpp        2015-08-26 19:24:41 UTC (rev 188979)
</span><span class="lines">@@ -310,7 +310,7 @@
</span><span class="cx"> case AllocatePropertyStorage:
</span><span class="cx"> case ReallocatePropertyStorage:
</span><span class="cx"> // These allocate but then run their own barrier.
</span><del>- insertBarrier(m_nodeIndex + 1, m_node->child1().node());
</del><ins>+ insertBarrierWithInvalidExit(m_nodeIndex + 1, Edge(m_node->child1().node(), KnownCellUse));
</ins><span class="cx"> m_node->setEpoch(Epoch());
</span><span class="cx"> break;
</span><span class="cx">
</span><span class="lines">@@ -474,19 +474,34 @@
</span><span class="cx">
</span><span class="cx"> if (verbose)
</span><span class="cx"> dataLog(" Inserting barrier.\n");
</span><del>- insertBarrier(m_nodeIndex, base.node());
</del><ins>+ insertBarrier(m_nodeIndex, base);
</ins><span class="cx"> }
</span><del>-
- void insertBarrier(unsigned nodeIndex, Node* base)
</del><ins>+
+ void insertBarrierWithInvalidExit(unsigned nodeIndex, Edge base)
</ins><span class="cx"> {
</span><ins>+ insertBarrier(nodeIndex, base, false);
+ }
+
+ void insertBarrier(unsigned nodeIndex, Edge base, bool exitOK = true)
+ {
</ins><span class="cx"> // If we're in global mode, we should only insert the barriers once we have converged.
</span><span class="cx"> if (!reallyInsertBarriers())
</span><span class="cx"> return;
</span><span class="cx">
</span><span class="cx"> // FIXME: We could support StoreBarrier(UntypedUse:). That would be sort of cool.
</span><span class="cx"> // But right now we don't need it.
</span><ins>+
+ // If the original edge was unchecked, we should also not have a check. We may be in a context
+ // where checks are not allowed. If we ever did have to insert a barrier at an ExitInvalid
+ // context and that barrier needed a check, then we could make that work by hoisting the check.
+ // That doesn't happen right now.
+ if (base.useKind() != KnownCellUse) {
+ DFG_ASSERT(m_graph, m_node, m_node->origin.exitOK);
+ base.setUseKind(CellUse);
+ }
+
</ins><span class="cx"> m_insertionSet.insertNode(
</span><del>- nodeIndex, SpecNone, StoreBarrier, m_node->origin, Edge(base, CellUse));
</del><ins>+ nodeIndex, SpecNone, StoreBarrier, m_node->origin.takeValidExit(exitOK), base);
</ins><span class="cx">
</span><span class="cx"> base->setEpoch(m_currentEpoch);
</span><span class="cx"> }
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGTypeCheckHoistingPhasecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGTypeCheckHoistingPhase.cpp (188978 => 188979)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGTypeCheckHoistingPhase.cpp        2015-08-26 19:21:19 UTC (rev 188978)
+++ trunk/Source/JavaScriptCore/dfg/DFGTypeCheckHoistingPhase.cpp        2015-08-26 19:24:41 UTC (rev 188979)
</span><span class="lines">@@ -108,8 +108,16 @@
</span><span class="cx"> BasicBlock* block = m_graph.block(blockIndex);
</span><span class="cx"> if (!block)
</span><span class="cx"> continue;
</span><ins>+ unsigned indexForChecks = UINT_MAX;
+ NodeOrigin originForChecks;
</ins><span class="cx"> for (unsigned indexInBlock = 0; indexInBlock < block->size(); ++indexInBlock) {
</span><span class="cx"> Node* node = block->at(indexInBlock);
</span><ins>+
+ if (node->origin.exitOK) {
+ indexForChecks = indexInBlock;
+ originForChecks = node->origin;
+ }
+
</ins><span class="cx"> // Be careful not to use 'node' after appending to the graph. In those switch
</span><span class="cx"> // cases where we need to append, we first carefully extract everything we need
</span><span class="cx"> // from the node, before doing any appending.
</span><span class="lines">@@ -170,13 +178,15 @@
</span><span class="cx">
</span><span class="cx"> if (iter->value.m_structure) {
</span><span class="cx"> insertionSet.insertNode(
</span><del>- indexInBlock, SpecNone, CheckStructure, origin,
</del><ins>+ indexForChecks, SpecNone, CheckStructure,
+ originForChecks.withSemantic(origin.semantic),
</ins><span class="cx"> OpInfo(m_graph.addStructureSet(iter->value.m_structure)),
</span><span class="cx"> Edge(child1.node(), CellUse));
</span><span class="cx"> } else if (iter->value.m_arrayModeIsValid) {
</span><span class="cx"> ASSERT(iter->value.m_arrayModeHoistingOkay);
</span><span class="cx"> insertionSet.insertNode(
</span><del>- indexInBlock, SpecNone, CheckArray, origin,
</del><ins>+ indexForChecks, SpecNone, CheckArray,
+ originForChecks.withSemantic(origin.semantic),
</ins><span class="cx"> OpInfo(iter->value.m_arrayMode.asWord()),
</span><span class="cx"> Edge(child1.node(), CellUse));
</span><span class="cx"> } else
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGValidatecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGValidate.cpp (188978 => 188979)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGValidate.cpp        2015-08-26 19:21:19 UTC (rev 188978)
+++ trunk/Source/JavaScriptCore/dfg/DFGValidate.cpp        2015-08-26 19:24:41 UTC (rev 188979)
</span><span class="lines">@@ -29,6 +29,7 @@
</span><span class="cx"> #if ENABLE(DFG_JIT)
</span><span class="cx">
</span><span class="cx"> #include "CodeBlockWithJITType.h"
</span><ins>+#include "DFGClobbersExitState.h"
</ins><span class="cx"> #include "DFGMayExit.h"
</span><span class="cx"> #include "JSCInlines.h"
</span><span class="cx"> #include <wtf/Assertions.h>
</span><span class="lines">@@ -185,13 +186,30 @@
</span><span class="cx">
</span><span class="cx"> for (size_t i = 0; i < block->size(); ++i) {
</span><span class="cx"> Node* node = block->at(i);
</span><ins>+
+ VALIDATE((node), node->origin.semantic.isSet() == node->origin.forExit.isSet());
+ VALIDATE((node), !(!node->origin.forExit.isSet() && node->origin.exitOK));
+ VALIDATE((node), !(mayExit(m_graph, node) == Exits && !node->origin.exitOK));
+
+ if (i) {
+ Node* previousNode = block->at(i - 1);
+ VALIDATE(
+ (node),
+ !clobbersExitState(m_graph, previousNode)
+ || !node->origin.exitOK
+ || node->op() == ExitOK
+ || node->origin.forExit != previousNode->origin.forExit);
+ VALIDATE(
+ (node),
+ !(!previousNode->origin.exitOK && node->origin.exitOK)
+ || node->op() == ExitOK
+ || node->origin.forExit != previousNode->origin.forExit);
+ }
</ins><span class="cx">
</span><del>- VALIDATE((node), node->origin.semantic.isSet() == node->origin.forExit.isSet());
- VALIDATE((node), !mayExit(m_graph, node) || node->origin.forExit.isSet());
</del><span class="cx"> VALIDATE((node), !node->hasStructure() || !!node->structure());
</span><span class="cx"> VALIDATE((node), !node->hasCellOperand() || node->cellOperand()->value().isCell());
</span><span class="cx"> VALIDATE((node), !node->hasCellOperand() || !!node->cellOperand()->value());
</span><del>-
</del><ins>+
</ins><span class="cx"> if (!(node->flags() & NodeHasVarArgs)) {
</span><span class="cx"> if (!node->child2())
</span><span class="cx"> VALIDATE((node), !node->child3());
</span><span class="lines">@@ -210,10 +228,13 @@
</span><span class="cx"> switch (node->child1().useKind()) {
</span><span class="cx"> case UntypedUse:
</span><span class="cx"> case CellUse:
</span><ins>+ case KnownCellUse:
</ins><span class="cx"> case Int32Use:
</span><ins>+ case KnownInt32Use:
</ins><span class="cx"> case Int52RepUse:
</span><span class="cx"> case DoubleRepUse:
</span><span class="cx"> case BooleanUse:
</span><ins>+ case KnownBooleanUse:
</ins><span class="cx"> break;
</span><span class="cx"> default:
</span><span class="cx"> VALIDATE((node), !"Bad use kind");
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreftlFTLCapabilitiescpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ftl/FTLCapabilities.cpp (188978 => 188979)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ftl/FTLCapabilities.cpp        2015-08-26 19:21:19 UTC (rev 188978)
+++ trunk/Source/JavaScriptCore/ftl/FTLCapabilities.cpp        2015-08-26 19:24:41 UTC (rev 188979)
</span><span class="lines">@@ -51,6 +51,7 @@
</span><span class="cx"> case GetStack:
</span><span class="cx"> case MovHint:
</span><span class="cx"> case ZombieHint:
</span><ins>+ case ExitOK:
</ins><span class="cx"> case Phantom:
</span><span class="cx"> case Flush:
</span><span class="cx"> case PhantomLocal:
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreftlFTLLowerDFGToLLVMcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp (188978 => 188979)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp        2015-08-26 19:21:19 UTC (rev 188978)
+++ trunk/Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp        2015-08-26 19:24:41 UTC (rev 188979)
</span><span class="lines">@@ -227,9 +227,8 @@
</span><span class="cx"> availabilityMap().m_locals.argument(i) =
</span><span class="cx"> Availability(FlushedAt(FlushedJSValue, virtualRegisterForArgument(i)));
</span><span class="cx"> }
</span><del>- m_codeOriginForExitTarget = CodeOrigin(0);
- m_codeOriginForExitProfile = CodeOrigin(0);
</del><span class="cx"> m_node = nullptr;
</span><ins>+ m_origin = NodeOrigin(CodeOrigin(0), CodeOrigin(0), true);
</ins><span class="cx"> for (unsigned i = codeBlock()->numParameters(); i--;) {
</span><span class="cx"> Node* node = m_graph.m_arguments[i];
</span><span class="cx"> VirtualRegister operand = virtualRegisterForArgument(i);
</span><span class="lines">@@ -393,8 +392,7 @@
</span><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> m_node = m_highBlock->at(nodeIndex);
</span><del>- m_codeOriginForExitProfile = m_node->origin.semantic;
- m_codeOriginForExitTarget = m_node->origin.forExit;
</del><ins>+ m_origin = m_node->origin;
</ins><span class="cx">
</span><span class="cx"> if (verboseCompilationEnabled())
</span><span class="cx"> dataLog("Lowering ", m_node, "\n");
</span><span class="lines">@@ -841,6 +839,7 @@
</span><span class="cx"> case LoopHint:
</span><span class="cx"> case MovHint:
</span><span class="cx"> case ZombieHint:
</span><ins>+ case ExitOK:
</ins><span class="cx"> case PhantomNewObject:
</span><span class="cx"> case PhantomNewFunction:
</span><span class="cx"> case PhantomCreateActivation:
</span><span class="lines">@@ -878,15 +877,18 @@
</span><span class="cx"> m_out.set(lowDouble(m_node->child1()), destination);
</span><span class="cx"> break;
</span><span class="cx"> case Int32Use:
</span><ins>+ case KnownInt32Use:
</ins><span class="cx"> m_out.set(lowInt32(m_node->child1()), destination);
</span><span class="cx"> break;
</span><span class="cx"> case Int52RepUse:
</span><span class="cx"> m_out.set(lowInt52(m_node->child1()), destination);
</span><span class="cx"> break;
</span><span class="cx"> case BooleanUse:
</span><ins>+ case KnownBooleanUse:
</ins><span class="cx"> m_out.set(lowBoolean(m_node->child1()), destination);
</span><span class="cx"> break;
</span><span class="cx"> case CellUse:
</span><ins>+ case KnownCellUse:
</ins><span class="cx"> m_out.set(lowCell(m_node->child1()), destination);
</span><span class="cx"> break;
</span><span class="cx"> case UntypedUse:
</span><span class="lines">@@ -4752,10 +4754,12 @@
</span><span class="cx"> {
</span><span class="cx"> if (verboseCompilationEnabled())
</span><span class="cx"> dataLog(" Invalidation point with availability: ", availabilityMap(), "\n");
</span><ins>+
+ DFG_ASSERT(m_graph, m_node, m_origin.exitOK);
</ins><span class="cx">
</span><span class="cx"> m_ftlState.jitCode->osrExit.append(OSRExit(
</span><span class="cx"> UncountableInvalidation, InvalidValueFormat, MethodOfGettingAValueProfile(),
</span><del>- m_codeOriginForExitTarget, m_codeOriginForExitProfile,
</del><ins>+ m_origin.forExit, m_origin.semantic,
</ins><span class="cx"> availabilityMap().m_locals.numberOfArguments(),
</span><span class="cx"> availabilityMap().m_locals.numberOfLocals()));
</span><span class="cx"> m_ftlState.finalizer->osrExit.append(OSRExitCompilationInfo());
</span><span class="lines">@@ -8092,6 +8096,8 @@
</span><span class="cx"> if (!m_availableRecoveries.isEmpty())
</span><span class="cx"> dataLog(" Available recoveries: ", listDump(m_availableRecoveries), "\n");
</span><span class="cx"> }
</span><ins>+
+ DFG_ASSERT(m_graph, m_node, m_origin.exitOK);
</ins><span class="cx">
</span><span class="cx"> if (doOSRExitFuzzing()) {
</span><span class="cx"> LValue numberOfFuzzChecks = m_out.add(
</span><span class="lines">@@ -8116,7 +8122,7 @@
</span><span class="cx">
</span><span class="cx"> m_ftlState.jitCode->osrExit.append(OSRExit(
</span><span class="cx"> kind, lowValue.format(), m_graph.methodOfGettingAValueProfileFor(highValue),
</span><del>- m_codeOriginForExitTarget, m_codeOriginForExitProfile,
</del><ins>+ m_origin.forExit, m_origin.semantic,
</ins><span class="cx"> availabilityMap().m_locals.numberOfArguments(),
</span><span class="cx"> availabilityMap().m_locals.numberOfLocals()));
</span><span class="cx"> m_ftlState.finalizer->osrExit.append(OSRExitCompilationInfo());
</span><span class="lines">@@ -8611,9 +8617,8 @@
</span><span class="cx"> BasicBlock* m_highBlock;
</span><span class="cx"> BasicBlock* m_nextHighBlock;
</span><span class="cx"> LBasicBlock m_nextLowBlock;
</span><del>-
- CodeOrigin m_codeOriginForExitTarget;
- CodeOrigin m_codeOriginForExitProfile;
</del><ins>+
+ NodeOrigin m_origin;
</ins><span class="cx"> unsigned m_nodeIndex;
</span><span class="cx"> Node* m_node;
</span><span class="cx">
</span></span></pre></div>
<a id="trunkSourceWTFChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WTF/ChangeLog (188978 => 188979)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WTF/ChangeLog        2015-08-26 19:21:19 UTC (rev 188978)
+++ trunk/Source/WTF/ChangeLog        2015-08-26 19:24:41 UTC (rev 188979)
</span><span class="lines">@@ -1,3 +1,13 @@
</span><ins>+2015-08-25 Filip Pizlo <fpizlo@apple.com>
+
+ Node::origin should be able to tell you if it's OK to exit
+ https://bugs.webkit.org/show_bug.cgi?id=145204
+
+ Reviewed by Geoffrey Garen.
+
+ * wtf/Insertion.h:
+ (WTF::executeInsertions): Add a useful assertion. This come into play because JSC will use UINT_MAX as "invalid index", and that ought to trigger this assertion.
+
</ins><span class="cx"> 2015-08-25 Csaba Osztrogonác <ossy@webkit.org>
</span><span class="cx">
</span><span class="cx"> Require GCC version at least 4.9
</span></span></pre></div>
<a id="trunkSourceWTFwtfInsertionh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WTF/wtf/Insertion.h (188978 => 188979)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WTF/wtf/Insertion.h        2015-08-26 19:21:19 UTC (rev 188978)
+++ trunk/Source/WTF/wtf/Insertion.h        2015-08-26 19:24:41 UTC (rev 188979)
</span><span class="lines">@@ -1,5 +1,5 @@
</span><span class="cx"> /*
</span><del>- * Copyright (C) 2013 Apple Inc. All rights reserved.
</del><ins>+ * Copyright (C) 2013, 2015 Apple Inc. All rights reserved.
</ins><span class="cx"> *
</span><span class="cx"> * Redistribution and use in source and binary forms, with or without
</span><span class="cx"> * modification, are permitted provided that the following conditions
</span><span class="lines">@@ -59,8 +59,10 @@
</span><span class="cx"> return;
</span><span class="cx"> target.grow(target.size() + insertions.size());
</span><span class="cx"> size_t lastIndex = target.size();
</span><ins>+ size_t originalTargetSize = target.size();
</ins><span class="cx"> for (size_t indexInInsertions = insertions.size(); indexInInsertions--;) {
</span><span class="cx"> ASSERT(!indexInInsertions || insertions[indexInInsertions].index() >= insertions[indexInInsertions - 1].index());
</span><ins>+ ASSERT_UNUSED(originalTargetSize, insertions[indexInInsertions].index() < originalTargetSize);
</ins><span class="cx"> size_t firstIndex = insertions[indexInInsertions].index() + indexInInsertions;
</span><span class="cx"> size_t indexOffset = indexInInsertions + 1;
</span><span class="cx"> for (size_t i = lastIndex; --i > firstIndex;)
</span></span></pre>
</div>
</div>
</body>
</html>