We should support CreateThis in the FTL
authorfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 22 Jul 2018 02:48:16 +0000 (02:48 +0000)
committerfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 22 Jul 2018 02:48:16 +0000 (02:48 +0000)
https://bugs.webkit.org/show_bug.cgi?id=164904

Reviewed by Yusuke Suzuki.
JSTests:

* microbenchmarks/polyvariant-get-by-id-shorter-tower.js: Added.
(polyvariant):
(Foo.prototype.func):
(Foo):
(foo):
(Bar.prototype.func):
(Bar):
(bar):
* microbenchmarks/polyvariant-get-by-id-tower.js: Added.
(polyvariant):
(Foo.prototype.func):
(Foo):
(foo):
(Bar.prototype.func):
(Bar):
(bar):
(Baz.prototype.func):
(Baz):
(baz):

Source/JavaScriptCore:

This started with Saam's patch to implement CreateThis in the FTL, but turned into a type
inference adventure.

CreateThis in the FTL was a massive regression in raytrace because it disturbed that
benchmark's extremely perverse way of winning at type inference:

- The benchmark wanted polyvariant devirtualization of an object construction helper. But,
  the polyvariant profiler wasn't powerful enough to reliably devirtualize that code. So, the
  benchmark was falling back to other mechanisms...

- The construction helper could not tier up into the FTL. When the DFG compiled it, it would
  see that the IC had 4 cases. That's too polymorphic for the DFG. So, the DFG would emit a
  GetById. Shortly after the DFG compile, that get_by_id would see many more cases, but now
  that the helper was compiled by the DFG, the baseline get_by_id would not see those cases.
  The DFG's GetById would "hide" those cases. The number of cases the DFG's GetById would see
  is larger than our polymorphic list limit (limit = 8, case count = 13, I think).

  Note that if the FTL compiles that construction helper, it sees the 4 cases, turns them
  into a MultiGetByOffset, then suffers from exits when the new cases hit, and then exits to
  baseline, which then sees those cases. Luckily, the FTL was not compiling the construction
  helper because it had a CreateThis.

- Compilations that inlined the construction helper would have gotten super lucky with
  parse-time constant folding, so they knew what structure the input to the get_by_id would
  have at parse time. This is only profitable if the get_by_id parsing computed a
  GetByIdStatus that had a finite number of cases. Because the 13 cases were being hidden by
  the DFG GetById and GetByIdStatus would only look at the baseline get_by_id, which had 4
  cases, we would indeed get a finite number of cases. The parser would then prune those
  cases to just one - based on its knowledge of the structure - and that would result in that
  get_by_id being folded at parse time to a constant.

- The subsequent op_call would inline based on parse-time knowledge of that constant.

This patch comprehensively fixes these issues, as well as other issues that come up along the
way. The short version is that raytrace was revealing sloppiness in our use of profiling for
type inference. This patch fixes the sloppiness by vastly expanding *polyvariant* profiling,
i.e. the profiling that considers call context. I was encouraged to do this by the fact that
even the old version of polyvariant profiling was a speed-up on JetStream, ARES-6, and
Speedometer 2 (it's easy to measure since it's a runtime flag). So, it seemed worthwhile to
attack raytrace's problem as a shortcoming of polyvariant profiling.

- Polyvariant profiling now consults every DFG or FTL code block that participated in any
  subset of the inline stack that includes the IC we're profiling. For example, if we have
  an inline stack like foo->bar->baz, with baz on top, then we will consult DFG or FTL
  compilations for foo, bar, and baz. In foo, we'll look up foo->bar->baz; in bar we'll look
  up bar->baz; etc. This fixes two problems encountered in raytrace. First, it ensures that
  a DFG GetById cannot hide anything from the profiling of that get_by_id, since the
  polyvariant profiling code will always consult it. Second, it enables raytrace to benefit
  from polyvariant profling. Previously, the polyvariant profiler would only look at the
  previous DFG compilation of foo and look up foo->bar->baz. But that only works if DFG-foo
  had inlined bar and then baz. It may not have done that, because those calls could have
  required polyvariant profiling that was only available in the FTL.

- A particularly interesting case is when some IC in foo-baseline is also available in
  foo-DFG. This case is encountered by the polyvariant profiler as it walks the inline stack.
  In the case of gathering profiling for foo-FTL, the polyvariant profiler finds foo-DFG via
  the trivial case of no inline stack. This also means that if foo ever gets inlined, we will
  find foo-DFG or foo-FTL in the final case of polyvariant profiling. In those cases, we now
  merge the IC of foo-baseline and foo-DFG. This avoids lots of unnecessary recompilations,
  because it warns us of historical polymorphism. Historical polymorphism usually means
  future polymorphism. IC status code already had some merging functionality, but I needed to
  beef it up a lot to make this work right.

- Inlining an inline cache now preserves as much information as profiling. One challenge of
  polyvariant profiling is that the FTL compile for bar (that includes bar->baz) could have
  inlined an inline cache based on polyvariant profiling. So, when the FTL compile for foo
  (that includes foo->bar->baz) asks bar what it knows about that IC inside bar->baz, it will
  say "I don't have such an IC". At this point the DFG compilation that included that IC that
  gave us the information that we used to inline the IC is no longer alive. To keep us from
  losing the information we learned about the IC, there is now a RecordedStatuses data
  structure that preserves the statuses we use for inlining ICs. We also filter those
  statuses according to things we learn from AI. This further reduces the risk of information
  about an IC being forgotten.

- Exit profiling now considers whether or not an exit happened from inline code. This
  protects us in the case where the not-inlined version of an IC exited a lot because of
  polymorphism that doesn't exist in the inlined version. So, when using polyvariant
  profiling data, we consider only inlined exits.

- CallLinkInfo now records when it's repatched to the virtual call thunk. Previously, this
  would clear the CallLinkInfo, so CallLinkStatus would fall back to the lastSeenCallee. It's
  surprising that we've had this bug.

Altogether this patch is performance-neutral in run-jsc-benchmarks, except for speed-ups in
microbenchmarks and a compile time regression. Octane/deltablue speeds up by ~5%.
Octane/raytrace is regressed by a minuscule amount, which we could make up by implementing
prototype access folding in the bytecode parser and constant folder. That would require some
significant new logic in GetByIdStatus. That would also require a new benchmark - we want to
have a test that captures raytrace's behavior in the case that the parser cannot fold the
get_by_id.

This change is a 1.2% regression on V8Spider-CompileTime. That's a smaller regression than
recent compile time progressions, so I think that's an OK trade-off. Also, I would expect a
compile time regression anytime we fill in FTL coverage.

This is neutral on JetStream, ARES-6, and Speedometer2. JetStream agrees that deltablue
speeds up and that raytrace slows down, but these changes balance out and don't affect the
overall score. In ARES-6, it looks like individual tests have some significant 1-2% speed-ups
or slow-downs. Air-steady is definitely ~1.5% faster. Basic-worst is probably 2% slower (p ~
0.1, so it's not very certain). The JetStream, ARES-6, and Speedometer2 overall scores don't
see a significant difference. In all three cases the difference is <0.5% with a high p value,
with JetStream and Speedometer2 being insignificant infinitesimal speed-ups and ARES-6 being
an insignificant infinitesimal slow-down.

Oh, and this change means that the FTL now has 100% coverage of JavaScript. You could do an
eval in a for-in loop in a for-of loop inside a with block that uses try/catch for control
flow in a polymorphic constructor while having a bad time, and we'll still compile it.

* CMakeLists.txt:
* JavaScriptCore.xcodeproj/project.pbxproj:
* Sources.txt:
* bytecode/ByValInfo.h:
* bytecode/BytecodeDumper.cpp:
(JSC::BytecodeDumper<Block>::printGetByIdCacheStatus):
(JSC::BytecodeDumper<Block>::printPutByIdCacheStatus):
(JSC::BytecodeDumper<Block>::printInByIdCacheStatus):
(JSC::BytecodeDumper<Block>::dumpCallLinkStatus):
(JSC::BytecodeDumper<CodeBlock>::dumpCallLinkStatus):
(JSC::BytecodeDumper<Block>::printCallOp):
(JSC::BytecodeDumper<Block>::dumpBytecode):
(JSC::BytecodeDumper<Block>::dumpBlock):
* bytecode/BytecodeDumper.h:
* bytecode/CallLinkInfo.h:
* bytecode/CallLinkStatus.cpp:
(JSC::CallLinkStatus::computeFor):
(JSC::CallLinkStatus::computeExitSiteData):
(JSC::CallLinkStatus::computeFromCallLinkInfo):
(JSC::CallLinkStatus::accountForExits):
(JSC::CallLinkStatus::finalize):
(JSC::CallLinkStatus::filter):
(JSC::CallLinkStatus::computeDFGStatuses): Deleted.
* bytecode/CallLinkStatus.h:
(JSC::CallLinkStatus::operator bool const):
(JSC::CallLinkStatus::operator! const): Deleted.
* bytecode/CallVariant.cpp:
(JSC::CallVariant::finalize):
(JSC::CallVariant::filter):
* bytecode/CallVariant.h:
(JSC::CallVariant::operator bool const):
(JSC::CallVariant::operator! const): Deleted.
* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::dumpBytecode):
(JSC::CodeBlock::propagateTransitions):
(JSC::CodeBlock::finalizeUnconditionally):
(JSC::CodeBlock::getICStatusMap):
(JSC::CodeBlock::resetJITData):
(JSC::CodeBlock::getStubInfoMap): Deleted.
(JSC::CodeBlock::getCallLinkInfoMap): Deleted.
(JSC::CodeBlock::getByValInfoMap): Deleted.
* bytecode/CodeBlock.h:
* bytecode/CodeOrigin.cpp:
(JSC::CodeOrigin::isApproximatelyEqualTo const):
(JSC::CodeOrigin::approximateHash const):
* bytecode/CodeOrigin.h:
(JSC::CodeOrigin::exitingInlineKind const):
* bytecode/DFGExitProfile.cpp:
(JSC::DFG::FrequentExitSite::dump const):
(JSC::DFG::ExitProfile::add):
* bytecode/DFGExitProfile.h:
(JSC::DFG::FrequentExitSite::FrequentExitSite):
(JSC::DFG::FrequentExitSite::operator== const):
(JSC::DFG::FrequentExitSite::subsumes const):
(JSC::DFG::FrequentExitSite::hash const):
(JSC::DFG::FrequentExitSite::inlineKind const):
(JSC::DFG::FrequentExitSite::withInlineKind const):
(JSC::DFG::QueryableExitProfile::hasExitSite const):
(JSC::DFG::QueryableExitProfile::hasExitSiteWithSpecificJITType const):
(JSC::DFG::QueryableExitProfile::hasExitSiteWithSpecificInlineKind const):
* bytecode/ExitFlag.cpp: Added.
(JSC::ExitFlag::dump const):
* bytecode/ExitFlag.h: Added.
(JSC::ExitFlag::ExitFlag):
(JSC::ExitFlag::operator| const):
(JSC::ExitFlag::operator|=):
(JSC::ExitFlag::operator& const):
(JSC::ExitFlag::operator&=):
(JSC::ExitFlag::operator bool const):
(JSC::ExitFlag::isSet const):
* bytecode/ExitingInlineKind.cpp: Added.
(WTF::printInternal):
* bytecode/ExitingInlineKind.h: Added.
* bytecode/GetByIdStatus.cpp:
(JSC::GetByIdStatus::computeFor):
(JSC::GetByIdStatus::computeForStubInfo):
(JSC::GetByIdStatus::slowVersion const):
(JSC::GetByIdStatus::markIfCheap):
(JSC::GetByIdStatus::finalize):
(JSC::GetByIdStatus::hasExitSite): Deleted.
* bytecode/GetByIdStatus.h:
* bytecode/GetByIdVariant.cpp:
(JSC::GetByIdVariant::markIfCheap):
(JSC::GetByIdVariant::finalize):
* bytecode/GetByIdVariant.h:
* bytecode/ICStatusMap.cpp: Added.
(JSC::ICStatusContext::get const):
(JSC::ICStatusContext::isInlined const):
(JSC::ICStatusContext::inlineKind const):
* bytecode/ICStatusMap.h: Added.
* bytecode/ICStatusUtils.cpp: Added.
(JSC::hasBadCacheExitSite):
* bytecode/ICStatusUtils.h:
* bytecode/InstanceOfStatus.cpp:
(JSC::InstanceOfStatus::computeFor):
* bytecode/InstanceOfStatus.h:
* bytecode/PolyProtoAccessChain.h:
* bytecode/PutByIdStatus.cpp:
(JSC::PutByIdStatus::hasExitSite):
(JSC::PutByIdStatus::computeFor):
(JSC::PutByIdStatus::slowVersion const):
(JSC::PutByIdStatus::markIfCheap):
(JSC::PutByIdStatus::finalize):
(JSC::PutByIdStatus::filter):
* bytecode/PutByIdStatus.h:
* bytecode/PutByIdVariant.cpp:
(JSC::PutByIdVariant::markIfCheap):
(JSC::PutByIdVariant::finalize):
* bytecode/PutByIdVariant.h:
(JSC::PutByIdVariant::structureSet const):
* bytecode/RecordedStatuses.cpp: Added.
(JSC::RecordedStatuses::operator=):
(JSC::RecordedStatuses::RecordedStatuses):
(JSC::RecordedStatuses::addCallLinkStatus):
(JSC::RecordedStatuses::addGetByIdStatus):
(JSC::RecordedStatuses::addPutByIdStatus):
(JSC::RecordedStatuses::markIfCheap):
(JSC::RecordedStatuses::finalizeWithoutDeleting):
(JSC::RecordedStatuses::finalize):
(JSC::RecordedStatuses::shrinkToFit):
* bytecode/RecordedStatuses.h: Added.
(JSC::RecordedStatuses::RecordedStatuses):
(JSC::RecordedStatuses::forEachVector):
* bytecode/StructureSet.cpp:
(JSC::StructureSet::markIfCheap const):
(JSC::StructureSet::isStillAlive const):
* bytecode/StructureSet.h:
* bytecode/TerminatedCodeOrigin.h: Added.
(JSC::TerminatedCodeOrigin::TerminatedCodeOrigin):
(JSC::TerminatedCodeOriginHashTranslator::hash):
(JSC::TerminatedCodeOriginHashTranslator::equal):
* bytecode/Watchpoint.cpp:
(WTF::printInternal):
* bytecode/Watchpoint.h:
* dfg/DFGAbstractInterpreter.h:
* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
(JSC::DFG::AbstractInterpreter<AbstractStateType>::filterICStatus):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::handleCall):
(JSC::DFG::ByteCodeParser::handleVarargsCall):
(JSC::DFG::ByteCodeParser::handleDOMJITGetter):
(JSC::DFG::ByteCodeParser::handleModuleNamespaceLoad):
(JSC::DFG::ByteCodeParser::handleGetById):
(JSC::DFG::ByteCodeParser::handlePutById):
(JSC::DFG::ByteCodeParser::parseBlock):
(JSC::DFG::ByteCodeParser::InlineStackEntry::InlineStackEntry):
(JSC::DFG::ByteCodeParser::InlineStackEntry::~InlineStackEntry):
(JSC::DFG::ByteCodeParser::parse):
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGClobbersExitState.cpp:
(JSC::DFG::clobbersExitState):
* dfg/DFGCommonData.h:
* dfg/DFGConstantFoldingPhase.cpp:
(JSC::DFG::ConstantFoldingPhase::foldConstants):
* dfg/DFGDesiredWatchpoints.h:
(JSC::DFG::SetPointerAdaptor::hasBeenInvalidated):
* dfg/DFGDoesGC.cpp:
(JSC::DFG::doesGC):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
* dfg/DFGGraph.cpp:
(JSC::DFG::Graph::dump):
* dfg/DFGMayExit.cpp:
* dfg/DFGNode.h:
(JSC::DFG::Node::hasCallLinkStatus):
(JSC::DFG::Node::callLinkStatus):
(JSC::DFG::Node::hasGetByIdStatus):
(JSC::DFG::Node::getByIdStatus):
(JSC::DFG::Node::hasPutByIdStatus):
(JSC::DFG::Node::putByIdStatus):
* dfg/DFGNodeType.h:
* dfg/DFGOSRExitBase.cpp:
(JSC::DFG::OSRExitBase::considerAddingAsFrequentExitSiteSlow):
* dfg/DFGObjectAllocationSinkingPhase.cpp:
* dfg/DFGPlan.cpp:
(JSC::DFG::Plan::reallyAdd):
(JSC::DFG::Plan::checkLivenessAndVisitChildren):
(JSC::DFG::Plan::finalizeInGC):
* dfg/DFGPlan.h:
* dfg/DFGPredictionPropagationPhase.cpp:
* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGStrengthReductionPhase.cpp:
(JSC::DFG::StrengthReductionPhase::handleNode):
* dfg/DFGWorklist.cpp:
(JSC::DFG::Worklist::removeDeadPlans):
* ftl/FTLAbstractHeapRepository.h:
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileNode):
(JSC::FTL::DFG::LowerDFGToB3::compileCreateThis):
(JSC::FTL::DFG::LowerDFGToB3::compileFilterICStatus):
* jit/PolymorphicCallStubRoutine.cpp:
(JSC::PolymorphicCallStubRoutine::hasEdges const):
(JSC::PolymorphicCallStubRoutine::edges const):
* jit/PolymorphicCallStubRoutine.h:
* profiler/ProfilerBytecodeSequence.cpp:
(JSC::Profiler::BytecodeSequence::BytecodeSequence):
* runtime/FunctionRareData.cpp:
(JSC::FunctionRareData::initializeObjectAllocationProfile):
* runtime/Options.h:

Source/WTF:

* wtf/TinyPtrSet.h:
(WTF::TinyPtrSet::operator!= const):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@234086 268f45cc-cd09-0410-ab3c-d52691b4dbfc

93 files changed:
JSTests/ChangeLog
JSTests/microbenchmarks/polyvariant-get-by-id-shorter-tower.js [new file with mode: 0644]
JSTests/microbenchmarks/polyvariant-get-by-id-tower.js [new file with mode: 0644]
PerformanceTests/ARES-6/ml/benchmark.js
Source/JavaScriptCore/API/tests/testapi.mm
Source/JavaScriptCore/CMakeLists.txt
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
Source/JavaScriptCore/Sources.txt
Source/JavaScriptCore/bytecode/ByValInfo.h
Source/JavaScriptCore/bytecode/BytecodeDumper.cpp
Source/JavaScriptCore/bytecode/BytecodeDumper.h
Source/JavaScriptCore/bytecode/CallLinkInfo.cpp
Source/JavaScriptCore/bytecode/CallLinkInfo.h
Source/JavaScriptCore/bytecode/CallLinkStatus.cpp
Source/JavaScriptCore/bytecode/CallLinkStatus.h
Source/JavaScriptCore/bytecode/CallVariant.cpp
Source/JavaScriptCore/bytecode/CallVariant.h
Source/JavaScriptCore/bytecode/CodeBlock.cpp
Source/JavaScriptCore/bytecode/CodeBlock.h
Source/JavaScriptCore/bytecode/CodeOrigin.cpp
Source/JavaScriptCore/bytecode/CodeOrigin.h
Source/JavaScriptCore/bytecode/DFGExitProfile.cpp
Source/JavaScriptCore/bytecode/DFGExitProfile.h
Source/JavaScriptCore/bytecode/ExitFlag.cpp [new file with mode: 0644]
Source/JavaScriptCore/bytecode/ExitFlag.h [new file with mode: 0644]
Source/JavaScriptCore/bytecode/ExitingInlineKind.cpp [new file with mode: 0644]
Source/JavaScriptCore/bytecode/ExitingInlineKind.h [new file with mode: 0644]
Source/JavaScriptCore/bytecode/GetByIdStatus.cpp
Source/JavaScriptCore/bytecode/GetByIdStatus.h
Source/JavaScriptCore/bytecode/GetByIdVariant.cpp
Source/JavaScriptCore/bytecode/GetByIdVariant.h
Source/JavaScriptCore/bytecode/ICStatusMap.cpp [new file with mode: 0644]
Source/JavaScriptCore/bytecode/ICStatusMap.h [new file with mode: 0644]
Source/JavaScriptCore/bytecode/ICStatusUtils.cpp [new file with mode: 0644]
Source/JavaScriptCore/bytecode/ICStatusUtils.h
Source/JavaScriptCore/bytecode/InByIdStatus.cpp
Source/JavaScriptCore/bytecode/InByIdStatus.h
Source/JavaScriptCore/bytecode/InByIdVariant.cpp
Source/JavaScriptCore/bytecode/InByIdVariant.h
Source/JavaScriptCore/bytecode/InstanceOfStatus.cpp
Source/JavaScriptCore/bytecode/InstanceOfStatus.h
Source/JavaScriptCore/bytecode/PutByIdStatus.cpp
Source/JavaScriptCore/bytecode/PutByIdStatus.h
Source/JavaScriptCore/bytecode/PutByIdVariant.cpp
Source/JavaScriptCore/bytecode/PutByIdVariant.h
Source/JavaScriptCore/bytecode/RecordedStatuses.cpp [new file with mode: 0644]
Source/JavaScriptCore/bytecode/RecordedStatuses.h [new file with mode: 0644]
Source/JavaScriptCore/bytecode/StructureSet.cpp
Source/JavaScriptCore/bytecode/StructureSet.h
Source/JavaScriptCore/bytecode/StructureStubInfo.cpp
Source/JavaScriptCore/bytecode/StructureStubInfo.h
Source/JavaScriptCore/bytecode/StubInfoSummary.cpp [new file with mode: 0644]
Source/JavaScriptCore/bytecode/StubInfoSummary.h [new file with mode: 0644]
Source/JavaScriptCore/bytecode/TerminatedCodeOrigin.h [new file with mode: 0644]
Source/JavaScriptCore/bytecode/Watchpoint.cpp
Source/JavaScriptCore/bytecode/Watchpoint.h
Source/JavaScriptCore/dfg/DFGAbstractInterpreter.h
Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
Source/JavaScriptCore/dfg/DFGArgumentsEliminationPhase.cpp
Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
Source/JavaScriptCore/dfg/DFGClobberize.h
Source/JavaScriptCore/dfg/DFGClobbersExitState.cpp
Source/JavaScriptCore/dfg/DFGCommonData.h
Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp
Source/JavaScriptCore/dfg/DFGDesiredWatchpoints.h
Source/JavaScriptCore/dfg/DFGDoesGC.cpp
Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
Source/JavaScriptCore/dfg/DFGGraph.cpp
Source/JavaScriptCore/dfg/DFGMayExit.cpp
Source/JavaScriptCore/dfg/DFGNode.h
Source/JavaScriptCore/dfg/DFGNodeType.h
Source/JavaScriptCore/dfg/DFGOSRExitBase.cpp
Source/JavaScriptCore/dfg/DFGObjectAllocationSinkingPhase.cpp
Source/JavaScriptCore/dfg/DFGPlan.cpp
Source/JavaScriptCore/dfg/DFGPlan.h
Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
Source/JavaScriptCore/dfg/DFGSafeToExecute.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
Source/JavaScriptCore/dfg/DFGStrengthReductionPhase.cpp
Source/JavaScriptCore/dfg/DFGVarargsForwardingPhase.cpp
Source/JavaScriptCore/dfg/DFGWorklist.cpp
Source/JavaScriptCore/ftl/FTLAbstractHeapRepository.h
Source/JavaScriptCore/ftl/FTLCapabilities.cpp
Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp
Source/JavaScriptCore/jit/PolymorphicCallStubRoutine.cpp
Source/JavaScriptCore/jit/PolymorphicCallStubRoutine.h
Source/JavaScriptCore/jit/Repatch.cpp
Source/JavaScriptCore/profiler/ProfilerBytecodeSequence.cpp
Source/JavaScriptCore/runtime/Options.h
Source/WTF/ChangeLog
Source/WTF/wtf/TinyPtrSet.h

index 29f8cb4..77719b4 100644 (file)
@@ -1,3 +1,30 @@
+2018-06-02  Filip Pizlo  <fpizlo@apple.com>
+
+        We should support CreateThis in the FTL
+        https://bugs.webkit.org/show_bug.cgi?id=164904
+
+        Reviewed by Yusuke Suzuki.
+
+        * microbenchmarks/polyvariant-get-by-id-shorter-tower.js: Added.
+        (polyvariant):
+        (Foo.prototype.func):
+        (Foo):
+        (foo):
+        (Bar.prototype.func):
+        (Bar):
+        (bar):
+        * microbenchmarks/polyvariant-get-by-id-tower.js: Added.
+        (polyvariant):
+        (Foo.prototype.func):
+        (Foo):
+        (foo):
+        (Bar.prototype.func):
+        (Bar):
+        (bar):
+        (Baz.prototype.func):
+        (Baz):
+        (baz):
+
 2018-07-20  Michael Saboff  <msaboff@apple.com>
 
         DFG AbstractInterpreter: CheckArray filters array modes for DirectArguments/ScopedArguments using only NonArray
diff --git a/JSTests/microbenchmarks/polyvariant-get-by-id-shorter-tower.js b/JSTests/microbenchmarks/polyvariant-get-by-id-shorter-tower.js
new file mode 100644 (file)
index 0000000..9618cd7
--- /dev/null
@@ -0,0 +1,46 @@
+(function() {
+    var globalO;
+    
+    function polyvariant()
+    {
+        return globalO.func();
+    }
+    
+    class Foo {
+        func()
+        {
+            return 42;
+        }
+    }
+    
+    var fooO = new Foo();
+    
+    function foo()
+    {
+        globalO = fooO;
+        return polyvariant();
+    }
+    
+    class Bar {
+        func()
+        {
+            return foo();
+        }
+    }
+    
+    var barO = new Bar();
+    
+    function bar()
+    {
+        globalO = barO;
+        return polyvariant();
+    }
+    
+    var count = 1000000;
+    var result = 0;
+    for (var i = 0; i < count; ++i)
+        result += bar();
+    
+    if (result != count * 42)
+        throw "Error: bad result: " + result;
+})();
diff --git a/JSTests/microbenchmarks/polyvariant-get-by-id-tower.js b/JSTests/microbenchmarks/polyvariant-get-by-id-tower.js
new file mode 100644 (file)
index 0000000..f08b3de
--- /dev/null
@@ -0,0 +1,61 @@
+(function() {
+    var globalO;
+    
+    function polyvariant()
+    {
+        return globalO.func();
+    }
+    
+    class Foo {
+        func()
+        {
+            return 42;
+        }
+    }
+    
+    var fooO = new Foo();
+    
+    function foo()
+    {
+        globalO = fooO;
+        return polyvariant();
+    }
+    
+    class Bar {
+        func()
+        {
+            return foo();
+        }
+    }
+    
+    var barO = new Bar();
+    
+    function bar()
+    {
+        globalO = barO;
+        return polyvariant();
+    }
+    
+    class Baz {
+        func()
+        {
+            return bar();
+        }
+    }
+    
+    var bazO = new Baz();
+    
+    function baz()
+    {
+        globalO = bazO;
+        return polyvariant();
+    }
+    
+    var count = 1000000;
+    var result = 0;
+    for (var i = 0; i < count; ++i)
+        result += baz();
+    
+    if (result != count * 42)
+        throw "Error: bad result: " + result;
+})();
index bd6ccd3..ed1d9a5 100644 (file)
@@ -161,7 +161,7 @@ function runBenchmark()
 
     let before = currentTime();
 
-    let benchmark = new Benchmark();
+    let benchmark = new MLBenchmark();
 
     for (let iteration = 0; iteration < numIterations; ++iteration)
         benchmark.runIteration();
index a1654ae..9100ad4 100644 (file)
@@ -554,7 +554,10 @@ static void runJITThreadLimitTests()
 
 static void testObjectiveCAPIMain()
 {
-    runJITThreadLimitTests();
+    // FIXME: Make this work again.
+    // https://bugs.webkit.org/show_bug.cgi?id=187886
+    if (false)
+        runJITThreadLimitTests();
 
     @autoreleasepool {
         JSVirtualMachine* vm = [[JSVirtualMachine alloc] init];
index a64fa1a..3691cf2 100644 (file)
@@ -440,9 +440,11 @@ set(JavaScriptCore_PRIVATE_FRAMEWORK_HEADERS
     bytecode/ExecutableToCodeBlockEdge.h
     bytecode/ExecutionCounter.h
     bytecode/ExitKind.h
+    bytecode/ExitingInlineKind.h
     bytecode/ExitingJITType.h
     bytecode/ExpressionRangeInfo.h
     bytecode/HandlerInfo.h
+    bytecode/ICStatusMap.h
     bytecode/InlineCallFrame.h
     bytecode/Instruction.h
     bytecode/InternalFunctionAllocationProfile.h
index e348113..ffd44b1 100644 (file)
@@ -1,3 +1,327 @@
+2018-06-02  Filip Pizlo  <fpizlo@apple.com>
+
+        We should support CreateThis in the FTL
+        https://bugs.webkit.org/show_bug.cgi?id=164904
+
+        Reviewed by Yusuke Suzuki.
+        
+        This started with Saam's patch to implement CreateThis in the FTL, but turned into a type
+        inference adventure.
+        
+        CreateThis in the FTL was a massive regression in raytrace because it disturbed that
+        benchmark's extremely perverse way of winning at type inference:
+        
+        - The benchmark wanted polyvariant devirtualization of an object construction helper. But,
+          the polyvariant profiler wasn't powerful enough to reliably devirtualize that code. So, the
+          benchmark was falling back to other mechanisms...
+        
+        - The construction helper could not tier up into the FTL. When the DFG compiled it, it would
+          see that the IC had 4 cases. That's too polymorphic for the DFG. So, the DFG would emit a
+          GetById. Shortly after the DFG compile, that get_by_id would see many more cases, but now
+          that the helper was compiled by the DFG, the baseline get_by_id would not see those cases.
+          The DFG's GetById would "hide" those cases. The number of cases the DFG's GetById would see
+          is larger than our polymorphic list limit (limit = 8, case count = 13, I think).
+          
+          Note that if the FTL compiles that construction helper, it sees the 4 cases, turns them
+          into a MultiGetByOffset, then suffers from exits when the new cases hit, and then exits to
+          baseline, which then sees those cases. Luckily, the FTL was not compiling the construction
+          helper because it had a CreateThis.
+        
+        - Compilations that inlined the construction helper would have gotten super lucky with
+          parse-time constant folding, so they knew what structure the input to the get_by_id would
+          have at parse time. This is only profitable if the get_by_id parsing computed a
+          GetByIdStatus that had a finite number of cases. Because the 13 cases were being hidden by
+          the DFG GetById and GetByIdStatus would only look at the baseline get_by_id, which had 4
+          cases, we would indeed get a finite number of cases. The parser would then prune those
+          cases to just one - based on its knowledge of the structure - and that would result in that
+          get_by_id being folded at parse time to a constant.
+        
+        - The subsequent op_call would inline based on parse-time knowledge of that constant.
+        
+        This patch comprehensively fixes these issues, as well as other issues that come up along the
+        way. The short version is that raytrace was revealing sloppiness in our use of profiling for
+        type inference. This patch fixes the sloppiness by vastly expanding *polyvariant* profiling,
+        i.e. the profiling that considers call context. I was encouraged to do this by the fact that
+        even the old version of polyvariant profiling was a speed-up on JetStream, ARES-6, and
+        Speedometer 2 (it's easy to measure since it's a runtime flag). So, it seemed worthwhile to
+        attack raytrace's problem as a shortcoming of polyvariant profiling.
+        
+        - Polyvariant profiling now consults every DFG or FTL code block that participated in any
+          subset of the inline stack that includes the IC we're profiling. For example, if we have
+          an inline stack like foo->bar->baz, with baz on top, then we will consult DFG or FTL
+          compilations for foo, bar, and baz. In foo, we'll look up foo->bar->baz; in bar we'll look
+          up bar->baz; etc. This fixes two problems encountered in raytrace. First, it ensures that
+          a DFG GetById cannot hide anything from the profiling of that get_by_id, since the
+          polyvariant profiling code will always consult it. Second, it enables raytrace to benefit
+          from polyvariant profling. Previously, the polyvariant profiler would only look at the
+          previous DFG compilation of foo and look up foo->bar->baz. But that only works if DFG-foo
+          had inlined bar and then baz. It may not have done that, because those calls could have
+          required polyvariant profiling that was only available in the FTL.
+          
+        - A particularly interesting case is when some IC in foo-baseline is also available in
+          foo-DFG. This case is encountered by the polyvariant profiler as it walks the inline stack.
+          In the case of gathering profiling for foo-FTL, the polyvariant profiler finds foo-DFG via
+          the trivial case of no inline stack. This also means that if foo ever gets inlined, we will
+          find foo-DFG or foo-FTL in the final case of polyvariant profiling. In those cases, we now
+          merge the IC of foo-baseline and foo-DFG. This avoids lots of unnecessary recompilations,
+          because it warns us of historical polymorphism. Historical polymorphism usually means
+          future polymorphism. IC status code already had some merging functionality, but I needed to
+          beef it up a lot to make this work right.
+        
+        - Inlining an inline cache now preserves as much information as profiling. One challenge of
+          polyvariant profiling is that the FTL compile for bar (that includes bar->baz) could have
+          inlined an inline cache based on polyvariant profiling. So, when the FTL compile for foo
+          (that includes foo->bar->baz) asks bar what it knows about that IC inside bar->baz, it will
+          say "I don't have such an IC". At this point the DFG compilation that included that IC that
+          gave us the information that we used to inline the IC is no longer alive. To keep us from
+          losing the information we learned about the IC, there is now a RecordedStatuses data
+          structure that preserves the statuses we use for inlining ICs. We also filter those
+          statuses according to things we learn from AI. This further reduces the risk of information
+          about an IC being forgotten.
+        
+        - Exit profiling now considers whether or not an exit happened from inline code. This
+          protects us in the case where the not-inlined version of an IC exited a lot because of
+          polymorphism that doesn't exist in the inlined version. So, when using polyvariant
+          profiling data, we consider only inlined exits.
+        
+        - CallLinkInfo now records when it's repatched to the virtual call thunk. Previously, this
+          would clear the CallLinkInfo, so CallLinkStatus would fall back to the lastSeenCallee. It's
+          surprising that we've had this bug.
+        
+        Altogether this patch is performance-neutral in run-jsc-benchmarks, except for speed-ups in
+        microbenchmarks and a compile time regression. Octane/deltablue speeds up by ~5%.
+        Octane/raytrace is regressed by a minuscule amount, which we could make up by implementing
+        prototype access folding in the bytecode parser and constant folder. That would require some
+        significant new logic in GetByIdStatus. That would also require a new benchmark - we want to
+        have a test that captures raytrace's behavior in the case that the parser cannot fold the
+        get_by_id.
+        
+        This change is a 1.2% regression on V8Spider-CompileTime. That's a smaller regression than
+        recent compile time progressions, so I think that's an OK trade-off. Also, I would expect a
+        compile time regression anytime we fill in FTL coverage.
+        
+        This is neutral on JetStream, ARES-6, and Speedometer2. JetStream agrees that deltablue
+        speeds up and that raytrace slows down, but these changes balance out and don't affect the
+        overall score. In ARES-6, it looks like individual tests have some significant 1-2% speed-ups
+        or slow-downs. Air-steady is definitely ~1.5% faster. Basic-worst is probably 2% slower (p ~
+        0.1, so it's not very certain). The JetStream, ARES-6, and Speedometer2 overall scores don't
+        see a significant difference. In all three cases the difference is <0.5% with a high p value,
+        with JetStream and Speedometer2 being insignificant infinitesimal speed-ups and ARES-6 being
+        an insignificant infinitesimal slow-down.
+        
+        Oh, and this change means that the FTL now has 100% coverage of JavaScript. You could do an
+        eval in a for-in loop in a for-of loop inside a with block that uses try/catch for control
+        flow in a polymorphic constructor while having a bad time, and we'll still compile it.
+
+        * CMakeLists.txt:
+        * JavaScriptCore.xcodeproj/project.pbxproj:
+        * Sources.txt:
+        * bytecode/ByValInfo.h:
+        * bytecode/BytecodeDumper.cpp:
+        (JSC::BytecodeDumper<Block>::printGetByIdCacheStatus):
+        (JSC::BytecodeDumper<Block>::printPutByIdCacheStatus):
+        (JSC::BytecodeDumper<Block>::printInByIdCacheStatus):
+        (JSC::BytecodeDumper<Block>::dumpCallLinkStatus):
+        (JSC::BytecodeDumper<CodeBlock>::dumpCallLinkStatus):
+        (JSC::BytecodeDumper<Block>::printCallOp):
+        (JSC::BytecodeDumper<Block>::dumpBytecode):
+        (JSC::BytecodeDumper<Block>::dumpBlock):
+        * bytecode/BytecodeDumper.h:
+        * bytecode/CallLinkInfo.h:
+        * bytecode/CallLinkStatus.cpp:
+        (JSC::CallLinkStatus::computeFor):
+        (JSC::CallLinkStatus::computeExitSiteData):
+        (JSC::CallLinkStatus::computeFromCallLinkInfo):
+        (JSC::CallLinkStatus::accountForExits):
+        (JSC::CallLinkStatus::finalize):
+        (JSC::CallLinkStatus::filter):
+        (JSC::CallLinkStatus::computeDFGStatuses): Deleted.
+        * bytecode/CallLinkStatus.h:
+        (JSC::CallLinkStatus::operator bool const):
+        (JSC::CallLinkStatus::operator! const): Deleted.
+        * bytecode/CallVariant.cpp:
+        (JSC::CallVariant::finalize):
+        (JSC::CallVariant::filter):
+        * bytecode/CallVariant.h:
+        (JSC::CallVariant::operator bool const):
+        (JSC::CallVariant::operator! const): Deleted.
+        * bytecode/CodeBlock.cpp:
+        (JSC::CodeBlock::dumpBytecode):
+        (JSC::CodeBlock::propagateTransitions):
+        (JSC::CodeBlock::finalizeUnconditionally):
+        (JSC::CodeBlock::getICStatusMap):
+        (JSC::CodeBlock::resetJITData):
+        (JSC::CodeBlock::getStubInfoMap): Deleted.
+        (JSC::CodeBlock::getCallLinkInfoMap): Deleted.
+        (JSC::CodeBlock::getByValInfoMap): Deleted.
+        * bytecode/CodeBlock.h:
+        * bytecode/CodeOrigin.cpp:
+        (JSC::CodeOrigin::isApproximatelyEqualTo const):
+        (JSC::CodeOrigin::approximateHash const):
+        * bytecode/CodeOrigin.h:
+        (JSC::CodeOrigin::exitingInlineKind const):
+        * bytecode/DFGExitProfile.cpp:
+        (JSC::DFG::FrequentExitSite::dump const):
+        (JSC::DFG::ExitProfile::add):
+        * bytecode/DFGExitProfile.h:
+        (JSC::DFG::FrequentExitSite::FrequentExitSite):
+        (JSC::DFG::FrequentExitSite::operator== const):
+        (JSC::DFG::FrequentExitSite::subsumes const):
+        (JSC::DFG::FrequentExitSite::hash const):
+        (JSC::DFG::FrequentExitSite::inlineKind const):
+        (JSC::DFG::FrequentExitSite::withInlineKind const):
+        (JSC::DFG::QueryableExitProfile::hasExitSite const):
+        (JSC::DFG::QueryableExitProfile::hasExitSiteWithSpecificJITType const):
+        (JSC::DFG::QueryableExitProfile::hasExitSiteWithSpecificInlineKind const):
+        * bytecode/ExitFlag.cpp: Added.
+        (JSC::ExitFlag::dump const):
+        * bytecode/ExitFlag.h: Added.
+        (JSC::ExitFlag::ExitFlag):
+        (JSC::ExitFlag::operator| const):
+        (JSC::ExitFlag::operator|=):
+        (JSC::ExitFlag::operator& const):
+        (JSC::ExitFlag::operator&=):
+        (JSC::ExitFlag::operator bool const):
+        (JSC::ExitFlag::isSet const):
+        * bytecode/ExitingInlineKind.cpp: Added.
+        (WTF::printInternal):
+        * bytecode/ExitingInlineKind.h: Added.
+        * bytecode/GetByIdStatus.cpp:
+        (JSC::GetByIdStatus::computeFor):
+        (JSC::GetByIdStatus::computeForStubInfo):
+        (JSC::GetByIdStatus::slowVersion const):
+        (JSC::GetByIdStatus::markIfCheap):
+        (JSC::GetByIdStatus::finalize):
+        (JSC::GetByIdStatus::hasExitSite): Deleted.
+        * bytecode/GetByIdStatus.h:
+        * bytecode/GetByIdVariant.cpp:
+        (JSC::GetByIdVariant::markIfCheap):
+        (JSC::GetByIdVariant::finalize):
+        * bytecode/GetByIdVariant.h:
+        * bytecode/ICStatusMap.cpp: Added.
+        (JSC::ICStatusContext::get const):
+        (JSC::ICStatusContext::isInlined const):
+        (JSC::ICStatusContext::inlineKind const):
+        * bytecode/ICStatusMap.h: Added.
+        * bytecode/ICStatusUtils.cpp: Added.
+        (JSC::hasBadCacheExitSite):
+        * bytecode/ICStatusUtils.h:
+        * bytecode/InstanceOfStatus.cpp:
+        (JSC::InstanceOfStatus::computeFor):
+        * bytecode/InstanceOfStatus.h:
+        * bytecode/PolyProtoAccessChain.h:
+        * bytecode/PutByIdStatus.cpp:
+        (JSC::PutByIdStatus::hasExitSite):
+        (JSC::PutByIdStatus::computeFor):
+        (JSC::PutByIdStatus::slowVersion const):
+        (JSC::PutByIdStatus::markIfCheap):
+        (JSC::PutByIdStatus::finalize):
+        (JSC::PutByIdStatus::filter):
+        * bytecode/PutByIdStatus.h:
+        * bytecode/PutByIdVariant.cpp:
+        (JSC::PutByIdVariant::markIfCheap):
+        (JSC::PutByIdVariant::finalize):
+        * bytecode/PutByIdVariant.h:
+        (JSC::PutByIdVariant::structureSet const):
+        * bytecode/RecordedStatuses.cpp: Added.
+        (JSC::RecordedStatuses::operator=):
+        (JSC::RecordedStatuses::RecordedStatuses):
+        (JSC::RecordedStatuses::addCallLinkStatus):
+        (JSC::RecordedStatuses::addGetByIdStatus):
+        (JSC::RecordedStatuses::addPutByIdStatus):
+        (JSC::RecordedStatuses::markIfCheap):
+        (JSC::RecordedStatuses::finalizeWithoutDeleting):
+        (JSC::RecordedStatuses::finalize):
+        (JSC::RecordedStatuses::shrinkToFit):
+        * bytecode/RecordedStatuses.h: Added.
+        (JSC::RecordedStatuses::RecordedStatuses):
+        (JSC::RecordedStatuses::forEachVector):
+        * bytecode/StructureSet.cpp:
+        (JSC::StructureSet::markIfCheap const):
+        (JSC::StructureSet::isStillAlive const):
+        * bytecode/StructureSet.h:
+        * bytecode/TerminatedCodeOrigin.h: Added.
+        (JSC::TerminatedCodeOrigin::TerminatedCodeOrigin):
+        (JSC::TerminatedCodeOriginHashTranslator::hash):
+        (JSC::TerminatedCodeOriginHashTranslator::equal):
+        * bytecode/Watchpoint.cpp:
+        (WTF::printInternal):
+        * bytecode/Watchpoint.h:
+        * dfg/DFGAbstractInterpreter.h:
+        * dfg/DFGAbstractInterpreterInlines.h:
+        (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+        (JSC::DFG::AbstractInterpreter<AbstractStateType>::filterICStatus):
+        * dfg/DFGByteCodeParser.cpp:
+        (JSC::DFG::ByteCodeParser::handleCall):
+        (JSC::DFG::ByteCodeParser::handleVarargsCall):
+        (JSC::DFG::ByteCodeParser::handleDOMJITGetter):
+        (JSC::DFG::ByteCodeParser::handleModuleNamespaceLoad):
+        (JSC::DFG::ByteCodeParser::handleGetById):
+        (JSC::DFG::ByteCodeParser::handlePutById):
+        (JSC::DFG::ByteCodeParser::parseBlock):
+        (JSC::DFG::ByteCodeParser::InlineStackEntry::InlineStackEntry):
+        (JSC::DFG::ByteCodeParser::InlineStackEntry::~InlineStackEntry):
+        (JSC::DFG::ByteCodeParser::parse):
+        * dfg/DFGClobberize.h:
+        (JSC::DFG::clobberize):
+        * dfg/DFGClobbersExitState.cpp:
+        (JSC::DFG::clobbersExitState):
+        * dfg/DFGCommonData.h:
+        * dfg/DFGConstantFoldingPhase.cpp:
+        (JSC::DFG::ConstantFoldingPhase::foldConstants):
+        * dfg/DFGDesiredWatchpoints.h:
+        (JSC::DFG::SetPointerAdaptor::hasBeenInvalidated):
+        * dfg/DFGDoesGC.cpp:
+        (JSC::DFG::doesGC):
+        * dfg/DFGFixupPhase.cpp:
+        (JSC::DFG::FixupPhase::fixupNode):
+        * dfg/DFGGraph.cpp:
+        (JSC::DFG::Graph::dump):
+        * dfg/DFGMayExit.cpp:
+        * dfg/DFGNode.h:
+        (JSC::DFG::Node::hasCallLinkStatus):
+        (JSC::DFG::Node::callLinkStatus):
+        (JSC::DFG::Node::hasGetByIdStatus):
+        (JSC::DFG::Node::getByIdStatus):
+        (JSC::DFG::Node::hasPutByIdStatus):
+        (JSC::DFG::Node::putByIdStatus):
+        * dfg/DFGNodeType.h:
+        * dfg/DFGOSRExitBase.cpp:
+        (JSC::DFG::OSRExitBase::considerAddingAsFrequentExitSiteSlow):
+        * dfg/DFGObjectAllocationSinkingPhase.cpp:
+        * dfg/DFGPlan.cpp:
+        (JSC::DFG::Plan::reallyAdd):
+        (JSC::DFG::Plan::checkLivenessAndVisitChildren):
+        (JSC::DFG::Plan::finalizeInGC):
+        * dfg/DFGPlan.h:
+        * dfg/DFGPredictionPropagationPhase.cpp:
+        * dfg/DFGSafeToExecute.h:
+        (JSC::DFG::safeToExecute):
+        * dfg/DFGSpeculativeJIT32_64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGSpeculativeJIT64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGStrengthReductionPhase.cpp:
+        (JSC::DFG::StrengthReductionPhase::handleNode):
+        * dfg/DFGWorklist.cpp:
+        (JSC::DFG::Worklist::removeDeadPlans):
+        * ftl/FTLAbstractHeapRepository.h:
+        * ftl/FTLCapabilities.cpp:
+        (JSC::FTL::canCompile):
+        * ftl/FTLLowerDFGToB3.cpp:
+        (JSC::FTL::DFG::LowerDFGToB3::compileNode):
+        (JSC::FTL::DFG::LowerDFGToB3::compileCreateThis):
+        (JSC::FTL::DFG::LowerDFGToB3::compileFilterICStatus):
+        * jit/PolymorphicCallStubRoutine.cpp:
+        (JSC::PolymorphicCallStubRoutine::hasEdges const):
+        (JSC::PolymorphicCallStubRoutine::edges const):
+        * jit/PolymorphicCallStubRoutine.h:
+        * profiler/ProfilerBytecodeSequence.cpp:
+        (JSC::Profiler::BytecodeSequence::BytecodeSequence):
+        * runtime/FunctionRareData.cpp:
+        (JSC::FunctionRareData::initializeObjectAllocationProfile):
+        * runtime/Options.h:
+
 2018-07-21  Yusuke Suzuki  <utatane.tea@gmail.com>
 
         [JSC] Use Function / ScopedLambda / RecursableLambda instead of std::function
index adda14c..325df6e 100644 (file)
                0F426A481460CBB300131F8F /* ValueRecovery.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F426A451460CBAB00131F8F /* ValueRecovery.h */; settings = {ATTRIBUTES = (Private, ); }; };
                0F426A491460CBB700131F8F /* VirtualRegister.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F426A461460CBAB00131F8F /* VirtualRegister.h */; settings = {ATTRIBUTES = (Private, ); }; };
                0F426A4B1460CD6E00131F8F /* DataFormat.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F426A4A1460CD6B00131F8F /* DataFormat.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               0F44767020C5E2B4008B2C36 /* StubInfoSummary.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F44766F20C5E2B2008B2C36 /* StubInfoSummary.h */; };
+               0F44A7B020BF68620022B171 /* ExitFlag.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F44A7AE20BF685F0022B171 /* ExitFlag.h */; };
+               0F44A7B120BF68C90022B171 /* ExitingInlineKind.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F44A7A720BF685D0022B171 /* ExitingInlineKind.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               0F44A7B220BF68CE0022B171 /* ICStatusMap.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F44A7AF20BF685F0022B171 /* ICStatusMap.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               0F44A7B320BF68D10022B171 /* RecordedStatuses.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F44A7AA20BF685E0022B171 /* RecordedStatuses.h */; };
+               0F44A7B420BF68D90022B171 /* TerminatedCodeOrigin.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F44A7A820BF685E0022B171 /* TerminatedCodeOrigin.h */; };
                0F4570391BE44C910062A629 /* AirEliminateDeadCode.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F4570371BE44C910062A629 /* AirEliminateDeadCode.h */; };
                0F45703D1BE45F0A0062A629 /* AirReportUsedRegisters.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F45703B1BE45F0A0062A629 /* AirReportUsedRegisters.h */; };
                0F4570411BE584CA0062A629 /* B3TimingScope.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F45703F1BE584CA0062A629 /* B3TimingScope.h */; };
                0F426A461460CBAB00131F8F /* VirtualRegister.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VirtualRegister.h; sourceTree = "<group>"; };
                0F426A4A1460CD6B00131F8F /* DataFormat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DataFormat.h; sourceTree = "<group>"; };
                0F42B3C0201EB50900357031 /* Allocator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Allocator.cpp; sourceTree = "<group>"; };
+               0F44766F20C5E2B2008B2C36 /* StubInfoSummary.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StubInfoSummary.h; sourceTree = "<group>"; };
+               0F44A7A720BF685D0022B171 /* ExitingInlineKind.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ExitingInlineKind.h; sourceTree = "<group>"; };
+               0F44A7A820BF685E0022B171 /* TerminatedCodeOrigin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TerminatedCodeOrigin.h; sourceTree = "<group>"; };
+               0F44A7A920BF685E0022B171 /* ExitFlag.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ExitFlag.cpp; sourceTree = "<group>"; };
+               0F44A7AA20BF685E0022B171 /* RecordedStatuses.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RecordedStatuses.h; sourceTree = "<group>"; };
+               0F44A7AB20BF685E0022B171 /* ICStatusMap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ICStatusMap.cpp; sourceTree = "<group>"; };
+               0F44A7AC20BF685F0022B171 /* ExitingInlineKind.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ExitingInlineKind.cpp; sourceTree = "<group>"; };
+               0F44A7AD20BF685F0022B171 /* RecordedStatuses.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RecordedStatuses.cpp; sourceTree = "<group>"; };
+               0F44A7AE20BF685F0022B171 /* ExitFlag.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ExitFlag.h; sourceTree = "<group>"; };
+               0F44A7AF20BF685F0022B171 /* ICStatusMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ICStatusMap.h; sourceTree = "<group>"; };
+               0F44A7B520C0BE3F0022B171 /* ICStatusUtils.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ICStatusUtils.cpp; sourceTree = "<group>"; };
                0F4570361BE44C910062A629 /* AirEliminateDeadCode.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AirEliminateDeadCode.cpp; path = b3/air/AirEliminateDeadCode.cpp; sourceTree = "<group>"; };
                0F4570371BE44C910062A629 /* AirEliminateDeadCode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AirEliminateDeadCode.h; path = b3/air/AirEliminateDeadCode.h; sourceTree = "<group>"; };
                0F45703A1BE45F0A0062A629 /* AirReportUsedRegisters.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AirReportUsedRegisters.cpp; path = b3/air/AirReportUsedRegisters.cpp; sourceTree = "<group>"; };
                0F9FB4F217FCB91700CB67F8 /* DFGStackLayoutPhase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DFGStackLayoutPhase.cpp; path = dfg/DFGStackLayoutPhase.cpp; sourceTree = "<group>"; };
                0F9FB4F317FCB91700CB67F8 /* DFGStackLayoutPhase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGStackLayoutPhase.h; path = dfg/DFGStackLayoutPhase.h; sourceTree = "<group>"; };
                0F9FC8C114E1B5FB00D52AE0 /* PutKind.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PutKind.h; sourceTree = "<group>"; };
+               0FA1265320C8DB98004B0D11 /* StubInfoSummary.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StubInfoSummary.cpp; sourceTree = "<group>"; };
                0FA2C17917D7CF84009D015F /* TestRunnerUtils.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TestRunnerUtils.cpp; sourceTree = "<group>"; };
                0FA2C17A17D7CF84009D015F /* TestRunnerUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TestRunnerUtils.h; sourceTree = "<group>"; };
                0FA581B7150E952A00B9A2D9 /* DFGNodeFlags.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DFGNodeFlags.cpp; path = dfg/DFGNodeFlags.cpp; sourceTree = "<group>"; };
                                0F60FE8E1FFC36FD0003320A /* ExecutableToCodeBlockEdge.h */,
                                0F56A1D415001CF2002992B1 /* ExecutionCounter.cpp */,
                                0F56A1D115000F31002992B1 /* ExecutionCounter.h */,
+                               0F44A7A920BF685E0022B171 /* ExitFlag.cpp */,
+                               0F44A7AE20BF685F0022B171 /* ExitFlag.h */,
+                               0F44A7AC20BF685F0022B171 /* ExitingInlineKind.cpp */,
+                               0F44A7A720BF685D0022B171 /* ExitingInlineKind.h */,
                                0F0332BF18ADFAE1005F979A /* ExitingJITType.cpp */,
                                0F3AC753188E5EC80032029F /* ExitingJITType.h */,
                                0FB105821675480C00F8AB6E /* ExitKind.cpp */,
                                0F0332C218B01763005F979A /* GetByIdVariant.h */,
                                14AD91081DCA92940014F9FE /* GlobalCodeBlock.h */,
                                0F0B83A814BCF55E00885B4F /* HandlerInfo.h */,
+                               0F44A7AB20BF685E0022B171 /* ICStatusMap.cpp */,
+                               0F44A7AF20BF685F0022B171 /* ICStatusMap.h */,
+                               0F44A7B520C0BE3F0022B171 /* ICStatusUtils.cpp */,
                                0FB399BD20AF6B380017E213 /* ICStatusUtils.h */,
                                E3305FB220B0F78800CEB82B /* InByIdStatus.cpp */,
                                E3305FAF20B0F78700CEB82B /* InByIdStatus.h */,
                                0F93B4A718B92C4D00178A3F /* PutByIdVariant.cpp */,
                                0F93B4A818B92C4D00178A3F /* PutByIdVariant.h */,
                                0F9FC8C114E1B5FB00D52AE0 /* PutKind.h */,
+                               0F44A7AD20BF685F0022B171 /* RecordedStatuses.cpp */,
+                               0F44A7AA20BF685E0022B171 /* RecordedStatuses.h */,
                                0FF60ABF16740F8100029779 /* ReduceWhitespace.cpp */,
                                0FF60AC016740F8100029779 /* ReduceWhitespace.h */,
                                0F5541AF1613C1FB00CE3E25 /* SpecialPointer.cpp */,
                                0F766D3715AE4A1A008F363E /* StructureStubClearingWatchpoint.h */,
                                BCCF0D0B0EF0B8A500413C8F /* StructureStubInfo.cpp */,
                                BCCF0D070EF0AAB900413C8F /* StructureStubInfo.h */,
+                               0FA1265320C8DB98004B0D11 /* StubInfoSummary.cpp */,
+                               0F44766F20C5E2B2008B2C36 /* StubInfoSummary.h */,
                                0F4A38F71C8E13DF00190318 /* SuperSampler.cpp */,
                                0F4A38F81C8E13DF00190318 /* SuperSampler.h */,
+                               0F44A7A820BF685E0022B171 /* TerminatedCodeOrigin.h */,
                                0F2D4DE519832DAC007D4B19 /* ToThisStatus.cpp */,
                                0F2D4DE619832DAC007D4B19 /* ToThisStatus.h */,
                                0F952ABA1B487A7700C367C5 /* TrackedReferences.cpp */,
                                53D444DC1DAF08AB00B92784 /* B3WasmAddressValue.h in Headers */,
                                5341FC721DAC343C00E7E4D7 /* B3WasmBoundsCheckValue.h in Headers */,
                                0F2C63B21E60AE4700C13839 /* B3Width.h in Headers */,
+                               0F44A7B220BF68CE0022B171 /* ICStatusMap.h in Headers */,
                                52678F8F1A031009006A306D /* BasicBlockLocation.h in Headers */,
                                147B83AC0E6DB8C9004775A4 /* BatchedTransitionOptimizer.h in Headers */,
                                86976E5F1FA3E8BC00E7C4E1 /* BigIntConstructor.h in Headers */,
                                86EC9DC61328DF82002B2AD7 /* DFGGenerationInfo.h in Headers */,
                                86EC9DC81328DF82002B2AD7 /* DFGGraph.h in Headers */,
                                0F2FCCFA18A60070001A27F8 /* DFGGraphSafepoint.h in Headers */,
+                               0F44A7B120BF68C90022B171 /* ExitingInlineKind.h in Headers */,
                                0FB17661196B8F9E0091052A /* DFGHeapLocation.h in Headers */,
                                0FC841691BA8C3210061837D /* DFGInferredTypeCheck.h in Headers */,
                                0FB14E211812570B009B6B4D /* DFGInlineCacheWrapper.h in Headers */,
                                A7A8AF3817ADB5F3005AB174 /* Float32Array.h in Headers */,
                                A7A8AF3917ADB5F3005AB174 /* Float64Array.h in Headers */,
                                0F24E54317EA9F5900ABB217 /* FPRInfo.h in Headers */,
+                               0F44A7B320BF68D10022B171 /* RecordedStatuses.h in Headers */,
                                E34EDBF71DB5FFC900DC87A5 /* FrameTracers.h in Headers */,
                                0F5513A61D5A682C00C32BD8 /* FreeList.h in Headers */,
                                0F6585E11EE0805A0095176D /* FreeListInlines.h in Headers */,
                                7013CA8C1B491A9400CAE613 /* JSJob.h in Headers */,
                                BC18C4160E16F5CD00B34460 /* JSLexicalEnvironment.h in Headers */,
                                BC18C4230E16F5CD00B34460 /* JSLock.h in Headers */,
+                               0F44A7B020BF68620022B171 /* ExitFlag.h in Headers */,
                                C25D709C16DE99F400FCA6BC /* JSManagedValue.h in Headers */,
                                2A4BB7F318A41179008A0FCD /* JSManagedValueInternal.h in Headers */,
                                A700874217CBE8EB00C3E643 /* JSMap.h in Headers */,
                                A7299DA217D12848005F5FF9 /* SetPrototype.h in Headers */,
                                0FEE98411A8865B700754E93 /* SetupVarargsFrame.h in Headers */,
                                DC17E8181C9C91D9008A6AB3 /* ShadowChicken.h in Headers */,
+                               0F44A7B420BF68D90022B171 /* TerminatedCodeOrigin.h in Headers */,
                                DC17E8191C9C91DB008A6AB3 /* ShadowChickenInlines.h in Headers */,
                                FE3022D31E3D73A500BAC493 /* SigillCrashAnalyzer.h in Headers */,
                                0F4D8C781FCA3CFA001D32AC /* SimpleMarkingConstraint.h in Headers */,
                                AD5B416F1EBAFB77008EFA43 /* WasmName.h in Headers */,
                                AD7B4B2E1FA3E29800C9DF79 /* WasmNameSection.h in Headers */,
                                ADD8FA461EB3079700DF542F /* WasmNameSectionParser.h in Headers */,
+                               0F44767020C5E2B4008B2C36 /* StubInfoSummary.h in Headers */,
                                5311BD4B1EA581E500525281 /* WasmOMGPlan.h in Headers */,
                                53C6FEEF1E8ADFA900B18425 /* WasmOpcodeOrigin.h in Headers */,
                                53B4BD121F68B32500D2BEA3 /* WasmOps.h in Headers */,
index 1da1f73..76dfee2 100644 (file)
@@ -212,13 +212,17 @@ bytecode/DirectEvalCodeCache.cpp
 bytecode/EvalCodeBlock.cpp
 bytecode/ExecutableToCodeBlockEdge.cpp
 bytecode/ExecutionCounter.cpp
+bytecode/ExitFlag.cpp
 bytecode/ExitKind.cpp
+bytecode/ExitingInlineKind.cpp
 bytecode/ExitingJITType.cpp
 bytecode/FullCodeOrigin.cpp
 bytecode/FunctionCodeBlock.cpp
 bytecode/GetByIdStatus.cpp
 bytecode/GetByIdVariant.cpp
 bytecode/GetterSetterAccessCase.cpp
+bytecode/ICStatusMap.cpp
+bytecode/ICStatusUtils.cpp
 bytecode/InByIdStatus.cpp
 bytecode/InByIdVariant.cpp
 bytecode/InlineAccess.cpp
@@ -247,12 +251,14 @@ bytecode/ProxyableAccessCase.cpp
 bytecode/PutByIdFlags.cpp
 bytecode/PutByIdStatus.cpp
 bytecode/PutByIdVariant.cpp
+bytecode/RecordedStatuses.cpp
 bytecode/ReduceWhitespace.cpp
 bytecode/SpecialPointer.cpp
 bytecode/SpeculatedType.cpp
 bytecode/StructureSet.cpp
 bytecode/StructureStubClearingWatchpoint.cpp
 bytecode/StructureStubInfo.cpp
+bytecode/StubInfoSummary.cpp
 bytecode/SuperSampler.cpp
 bytecode/ToThisStatus.cpp
 bytecode/TrackedReferences.cpp
index ee09908..5d5fe27 100644 (file)
@@ -266,12 +266,6 @@ inline unsigned getByValInfoBytecodeIndex(ByValInfo* info)
     return info->bytecodeIndex;
 }
 
-typedef HashMap<CodeOrigin, ByValInfo*, CodeOriginApproximateHash> ByValInfoMap;
-
-#else // ENABLE(JIT)
-
-typedef HashMap<int, void*> ByValInfoMap;
-
 #endif // ENABLE(JIT)
 
 } // namespace JSC
index eba04d9..1eddbf3 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2017 Yusuke Suzuki <utatane.tea@gmail.com>
- * Copyright (C) 2017 Apple Inc. All rights reserved.
+ * Copyright (C) 2017-2018 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -402,7 +402,7 @@ static void dumpChain(PrintStream& out, StructureChain* chain, const Identifier&
 }
 
 template<class Block>
-void BytecodeDumper<Block>::printGetByIdCacheStatus(PrintStream& out, int location, const StubInfoMap& map)
+void BytecodeDumper<Block>::printGetByIdCacheStatus(PrintStream& out, int location, const ICStatusMap& statusMap)
 {
     const auto* instruction = instructionsBegin() + location;
 
@@ -422,7 +422,7 @@ void BytecodeDumper<Block>::printGetByIdCacheStatus(PrintStream& out, int locati
     }
 
 #if ENABLE(JIT)
-    if (StructureStubInfo* stubPtr = map.get(CodeOrigin(location))) {
+    if (StructureStubInfo* stubPtr = statusMap.get(CodeOrigin(location)).stubInfo) {
         StructureStubInfo& stubInfo = *stubPtr;
         if (stubInfo.resetByGC)
             out.print(" (Reset By GC)");
@@ -463,12 +463,12 @@ void BytecodeDumper<Block>::printGetByIdCacheStatus(PrintStream& out, int locati
         out.printf(")");
     }
 #else
-    UNUSED_PARAM(map);
+    UNUSED_PARAM(statusMap);
 #endif
 }
 
 template<class Block>
-void BytecodeDumper<Block>::printPutByIdCacheStatus(PrintStream& out, int location, const StubInfoMap& map)
+void BytecodeDumper<Block>::printPutByIdCacheStatus(PrintStream& out, int location, const ICStatusMap& statusMap)
 {
     const auto* instruction = instructionsBegin() + location;
 
@@ -496,7 +496,7 @@ void BytecodeDumper<Block>::printPutByIdCacheStatus(PrintStream& out, int locati
     }
 
 #if ENABLE(JIT)
-    if (StructureStubInfo* stubPtr = map.get(CodeOrigin(location))) {
+    if (StructureStubInfo* stubPtr = statusMap.get(CodeOrigin(location)).stubInfo) {
         StructureStubInfo& stubInfo = *stubPtr;
         if (stubInfo.resetByGC)
             out.print(" (Reset By GC)");
@@ -522,12 +522,12 @@ void BytecodeDumper<Block>::printPutByIdCacheStatus(PrintStream& out, int locati
         out.printf(")");
     }
 #else
-    UNUSED_PARAM(map);
+    UNUSED_PARAM(statusMap);
 #endif
 }
 
 template<class Block>
-void BytecodeDumper<Block>::printInByIdCacheStatus(PrintStream& out, int location, const StubInfoMap& map)
+void BytecodeDumper<Block>::printInByIdCacheStatus(PrintStream& out, int location, const ICStatusMap& statusMap)
 {
     const auto* instruction = instructionsBegin() + location;
 
@@ -536,7 +536,7 @@ void BytecodeDumper<Block>::printInByIdCacheStatus(PrintStream& out, int locatio
     UNUSED_PARAM(ident); // tell the compiler to shut up in certain platform configurations.
 
 #if ENABLE(JIT)
-    if (StructureStubInfo* stubPtr = map.get(CodeOrigin(location))) {
+    if (StructureStubInfo* stubPtr = statusMap.get(CodeOrigin(location)).stubInfo) {
         StructureStubInfo& stubInfo = *stubPtr;
         if (stubInfo.resetByGC)
             out.print(" (Reset By GC)");
@@ -575,26 +575,26 @@ void BytecodeDumper<Block>::printInByIdCacheStatus(PrintStream& out, int locatio
     }
 #else
     UNUSED_PARAM(out);
-    UNUSED_PARAM(map);
+    UNUSED_PARAM(statusMap);
 #endif
 }
 
 #if ENABLE(JIT)
 template<typename Block>
-void BytecodeDumper<Block>::dumpCallLinkStatus(PrintStream&, unsigned, const CallLinkInfoMap&)
+void BytecodeDumper<Block>::dumpCallLinkStatus(PrintStream&, unsigned, const ICStatusMap&)
 {
 }
 
 template<>
-void BytecodeDumper<CodeBlock>::dumpCallLinkStatus(PrintStream& out, unsigned location, const CallLinkInfoMap& map)
+void BytecodeDumper<CodeBlock>::dumpCallLinkStatus(PrintStream& out, unsigned location, const ICStatusMap& statusMap)
 {
     if (block()->jitType() != JITCode::FTLJIT)
-        out.print(" status(", CallLinkStatus::computeFor(block(), location, map), ")");
+        out.print(" status(", CallLinkStatus::computeFor(block(), location, statusMap), ")");
 }
 #endif
 
 template<class Block>
-void BytecodeDumper<Block>::printCallOp(PrintStream& out, int location, const typename Block::Instruction*& it, const char* op, CacheDumpMode cacheDumpMode, bool& hasPrintedProfiling, const CallLinkInfoMap& map)
+void BytecodeDumper<Block>::printCallOp(PrintStream& out, int location, const typename Block::Instruction*& it, const char* op, CacheDumpMode cacheDumpMode, bool& hasPrintedProfiling, const ICStatusMap& statusMap)
 {
     int dst = (++it)->u.operand;
     int func = (++it)->u.operand;
@@ -613,7 +613,7 @@ void BytecodeDumper<Block>::printCallOp(PrintStream& out, int location, const ty
                 out.printf(" llint(%p)", object);
         }
 #if ENABLE(JIT)
-        if (CallLinkInfo* info = map.get(CodeOrigin(location))) {
+        if (CallLinkInfo* info = statusMap.get(CodeOrigin(location)).callLinkInfo) {
             if (info->haveLastSeenCallee()) {
                 JSObject* object = info->lastSeenCallee();
                 if (auto* function = jsDynamicCast<JSFunction*>(*vm(), object))
@@ -623,9 +623,9 @@ void BytecodeDumper<Block>::printCallOp(PrintStream& out, int location, const ty
             }
         }
 
-        dumpCallLinkStatus(out, location, map);
+        dumpCallLinkStatus(out, location, statusMap);
 #else
-        UNUSED_PARAM(map);
+        UNUSED_PARAM(statusMap);
 #endif
     }
     ++it;
@@ -653,7 +653,7 @@ void BytecodeDumper<Block>::printLocationOpAndRegisterOperand(PrintStream& out,
 }
 
 template<class Block>
-void BytecodeDumper<Block>::dumpBytecode(PrintStream& out, const typename Block::Instruction* begin, const typename Block::Instruction*& it, const StubInfoMap& stubInfos, const CallLinkInfoMap& callLinkInfos)
+void BytecodeDumper<Block>::dumpBytecode(PrintStream& out, const typename Block::Instruction* begin, const typename Block::Instruction*& it, const ICStatusMap& statusMap)
 {
     int location = it - begin;
     bool hasPrintedProfiling = false;
@@ -1042,7 +1042,7 @@ void BytecodeDumper<Block>::dumpBytecode(PrintStream& out, const typename Block:
         int id0 = (++it)->u.operand;
         printLocationAndOp(out, location, it, "in_by_id");
         out.printf("%s, %s, %s", registerName(r0).data(), registerName(r1).data(), idName(id0, identifier(id0)).data());
-        printInByIdCacheStatus(out, location, stubInfos);
+        printInByIdCacheStatus(out, location, statusMap);
         break;
     }
     case op_in_by_val: {
@@ -1066,7 +1066,7 @@ void BytecodeDumper<Block>::dumpBytecode(PrintStream& out, const typename Block:
         printLocationAndOp(out, location, it, "get_by_id_direct");
         out.printf("%s, %s, %s", registerName(r0).data(), registerName(r1).data(), idName(id0, identifier(id0)).data());
         it += 2; // Increment up to the value profiler.
-        printGetByIdCacheStatus(out, location, stubInfos);
+        printGetByIdCacheStatus(out, location, statusMap);
         dumpValueProfiling(out, it, hasPrintedProfiling);
         break;
     }
@@ -1075,7 +1075,7 @@ void BytecodeDumper<Block>::dumpBytecode(PrintStream& out, const typename Block:
     case op_get_by_id_unset:
     case op_get_array_length: {
         printGetByIdOp(out, location, it);
-        printGetByIdCacheStatus(out, location, stubInfos);
+        printGetByIdCacheStatus(out, location, statusMap);
         dumpValueProfiling(out, it, hasPrintedProfiling);
         break;
     }
@@ -1101,7 +1101,7 @@ void BytecodeDumper<Block>::dumpBytecode(PrintStream& out, const typename Block:
     }
     case op_put_by_id: {
         printPutByIdOp(out, location, it, "put_by_id");
-        printPutByIdCacheStatus(out, location, stubInfos);
+        printPutByIdCacheStatus(out, location, statusMap);
         break;
     }
     case op_put_by_id_with_this: {
@@ -1447,15 +1447,15 @@ void BytecodeDumper<Block>::dumpBytecode(PrintStream& out, const typename Block:
         break;
     }
     case op_call: {
-        printCallOp(out, location, it, "call", DumpCaches, hasPrintedProfiling, callLinkInfos);
+        printCallOp(out, location, it, "call", DumpCaches, hasPrintedProfiling, statusMap);
         break;
     }
     case op_tail_call: {
-        printCallOp(out, location, it, "tail_call", DumpCaches, hasPrintedProfiling, callLinkInfos);
+        printCallOp(out, location, it, "tail_call", DumpCaches, hasPrintedProfiling, statusMap);
         break;
     }
     case op_call_eval: {
-        printCallOp(out, location, it, "call_eval", DontDumpCaches, hasPrintedProfiling, callLinkInfos);
+        printCallOp(out, location, it, "call_eval", DontDumpCaches, hasPrintedProfiling, statusMap);
         break;
     }
 
@@ -1494,7 +1494,7 @@ void BytecodeDumper<Block>::dumpBytecode(PrintStream& out, const typename Block:
         break;
     }
     case op_construct: {
-        printCallOp(out, location, it, "construct", DumpCaches, hasPrintedProfiling, callLinkInfos);
+        printCallOp(out, location, it, "construct", DumpCaches, hasPrintedProfiling, statusMap);
         break;
     }
     case op_strcat: {
@@ -1749,10 +1749,10 @@ void BytecodeDumper<Block>::dumpBytecode(PrintStream& out, const typename Block:
 }
 
 template<class Block>
-void BytecodeDumper<Block>::dumpBytecode(Block* block, PrintStream& out, const typename Block::Instruction* begin, const typename Block::Instruction*& it, const StubInfoMap& stubInfos, const CallLinkInfoMap& callLinkInfos)
+void BytecodeDumper<Block>::dumpBytecode(Block* block, PrintStream& out, const typename Block::Instruction* begin, const typename Block::Instruction*& it, const ICStatusMap& statusMap)
 {
     BytecodeDumper dumper(block, begin);
-    dumper.dumpBytecode(out, begin, it, stubInfos, callLinkInfos);
+    dumper.dumpBytecode(out, begin, it, statusMap);
 }
 
 template<class Block>
@@ -1848,7 +1848,7 @@ void BytecodeDumper<Block>::dumpStringSwitchJumpTables(PrintStream& out)
 }
 
 template<class Block>
-void BytecodeDumper<Block>::dumpBlock(Block* block, const typename Block::UnpackedInstructions& instructions, PrintStream& out, const StubInfoMap& stubInfos, const CallLinkInfoMap& callLinkInfos)
+void BytecodeDumper<Block>::dumpBlock(Block* block, const typename Block::UnpackedInstructions& instructions, PrintStream& out, const ICStatusMap& statusMap)
 {
     size_t instructionCount = 0;
 
@@ -1868,7 +1868,7 @@ void BytecodeDumper<Block>::dumpBlock(Block* block, const typename Block::Unpack
     const auto* end = instructions.end();
     BytecodeDumper<Block> dumper(block, begin);
     for (const auto* it = begin; it != end; ++it)
-        dumper.dumpBytecode(out, begin, it, stubInfos, callLinkInfos);
+        dumper.dumpBytecode(out, begin, it, statusMap);
 
     dumper.dumpIdentifiers(out);
     dumper.dumpConstants(out);
index deb4788..d811a8d 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2017 Yusuke Suzuki <utatane.tea@gmail.com>
+ * Copyright (C) 2018 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -26,6 +27,7 @@
 #pragma once
 
 #include "CallLinkInfo.h"
+#include "ICStatusMap.h"
 #include "StructureStubInfo.h"
 
 namespace JSC {
@@ -37,8 +39,8 @@ class BytecodeDumper {
 public:
     typedef typename Block::Instruction Instruction;
 
-    static void dumpBytecode(Block*, PrintStream& out, const Instruction* begin, const Instruction*& it, const StubInfoMap& = StubInfoMap(), const CallLinkInfoMap& = CallLinkInfoMap());
-    static void dumpBlock(Block*, const typename Block::UnpackedInstructions&, PrintStream& out, const StubInfoMap& = StubInfoMap(), const CallLinkInfoMap& = CallLinkInfoMap());
+    static void dumpBytecode(Block*, PrintStream& out, const Instruction* begin, const Instruction*& it, const ICStatusMap& statusMap = ICStatusMap());
+    static void dumpBlock(Block*, const typename Block::UnpackedInstructions&, PrintStream& out, const ICStatusMap& statusMap = ICStatusMap());
 
 private:
     BytecodeDumper(Block* block, const Instruction* instructionsBegin)
@@ -68,14 +70,14 @@ private:
     void printConditionalJump(PrintStream& out, const Instruction*, const Instruction*& it, int location, const char* op);
     void printCompareJump(PrintStream& out, const Instruction*, const Instruction*& it, int location, const char* op);
     void printGetByIdOp(PrintStream& out, int location, const Instruction*& it);
-    void printGetByIdCacheStatus(PrintStream& out, int location, const StubInfoMap&);
-    void printPutByIdCacheStatus(PrintStream& out, int location, const StubInfoMap&);
-    void printInByIdCacheStatus(PrintStream& out, int location, const StubInfoMap&);
+    void printGetByIdCacheStatus(PrintStream& out, int location, const ICStatusMap&);
+    void printPutByIdCacheStatus(PrintStream& out, int location, const ICStatusMap&);
+    void printInByIdCacheStatus(PrintStream& out, int location, const ICStatusMap&);
     enum CacheDumpMode { DumpCaches, DontDumpCaches };
-    void printCallOp(PrintStream& out, int location, const Instruction*& it, const char* op, CacheDumpMode, bool& hasPrintedProfiling, const CallLinkInfoMap&);
+    void printCallOp(PrintStream& out, int location, const Instruction*& it, const char* op, CacheDumpMode, bool& hasPrintedProfiling, const ICStatusMap&);
     void printPutByIdOp(PrintStream& out, int location, const Instruction*& it, const char* op);
     void printLocationOpAndRegisterOperand(PrintStream& out, int location, const Instruction*& it, const char* op, int operand);
-    void dumpBytecode(PrintStream& out, const Instruction* begin, const Instruction*& it, const StubInfoMap&, const CallLinkInfoMap&);
+    void dumpBytecode(PrintStream& out, const Instruction* begin, const Instruction*& it, const ICStatusMap&);
 
     void dumpValueProfiling(PrintStream&, const Instruction*&, bool& hasPrintedProfiling);
     void dumpArrayProfiling(PrintStream&, const Instruction*&, bool& hasPrintedProfiling);
@@ -84,7 +86,7 @@ private:
     void* actualPointerFor(Special::Pointer) const;
 
 #if ENABLE(JIT)
-    void dumpCallLinkStatus(PrintStream&, unsigned location, const CallLinkInfoMap&);
+    void dumpCallLinkStatus(PrintStream&, unsigned location, const ICStatusMap&);
 #endif
 
     Block* m_block;
index b5eb6f0..c0a3778 100644 (file)
@@ -59,6 +59,7 @@ CallLinkInfo::CallLinkInfo()
     : m_hasSeenShouldRepatch(false)
     , m_hasSeenClosure(false)
     , m_clearedByGC(false)
+    , m_clearedByVirtual(false)
     , m_allowStubs(true)
     , m_isLinked(false)
     , m_callType(None)
@@ -220,7 +221,7 @@ void CallLinkInfo::visitWeak(VM& vm)
             if (!stub()->visitWeak(vm)) {
                 if (Options::verboseOSR()) {
                     dataLog(
-                        "Clearing closure call to ",
+                        "At ", m_codeOrigin, ", ", RawPointer(this), ": clearing call stub to ",
                         listDump(stub()->variants()), ", stub routine ", RawPointer(stub()),
                         ".\n");
                 }
index 68b97bf..8018955 100644 (file)
@@ -264,7 +264,17 @@ public:
     {
         return m_clearedByGC;
     }
+    
+    bool clearedByVirtual()
+    {
+        return m_clearedByVirtual;
+    }
 
+    void setClearedByVirtual()
+    {
+        m_clearedByVirtual = true;
+    }
+    
     void setCallType(CallType callType)
     {
         m_callType = callType;
@@ -338,6 +348,7 @@ private:
     bool m_hasSeenShouldRepatch : 1;
     bool m_hasSeenClosure : 1;
     bool m_clearedByGC : 1;
+    bool m_clearedByVirtual : 1;
     bool m_allowStubs : 1;
     bool m_isLinked : 1;
     unsigned m_callType : 4; // CallType
@@ -352,12 +363,6 @@ inline CodeOrigin getCallLinkInfoCodeOrigin(CallLinkInfo& callLinkInfo)
     return callLinkInfo.codeOrigin();
 }
 
-typedef HashMap<CodeOrigin, CallLinkInfo*, CodeOriginApproximateHash> CallLinkInfoMap;
-
-#else // ENABLE(JIT)
-
-typedef HashMap<int, void*> CallLinkInfoMap;
-
 #endif // ENABLE(JIT)
 
 } // namespace JSC
index d0dc3a3..aadf3ea 100644 (file)
@@ -77,7 +77,8 @@ CallLinkStatus CallLinkStatus::computeFromLLInt(const ConcurrentJSLocker&, CodeB
 }
 
 CallLinkStatus CallLinkStatus::computeFor(
-    CodeBlock* profiledBlock, unsigned bytecodeIndex, const CallLinkInfoMap& map)
+    CodeBlock* profiledBlock, unsigned bytecodeIndex, const ICStatusMap& map,
+    ExitSiteData exitSiteData)
 {
     ConcurrentJSLocker locker(profiledBlock->m_lock);
     
@@ -85,9 +86,7 @@ CallLinkStatus CallLinkStatus::computeFor(
     UNUSED_PARAM(bytecodeIndex);
     UNUSED_PARAM(map);
 #if ENABLE(DFG_JIT)
-    ExitSiteData exitSiteData = computeExitSiteData(profiledBlock, bytecodeIndex);
-    
-    CallLinkInfo* callLinkInfo = map.get(CodeOrigin(bytecodeIndex));
+    CallLinkInfo* callLinkInfo = map.get(CodeOrigin(bytecodeIndex)).callLinkInfo;
     if (!callLinkInfo) {
         if (exitSiteData.takesSlowPath)
             return takesSlowPath();
@@ -100,6 +99,12 @@ CallLinkStatus CallLinkStatus::computeFor(
 #endif
 }
 
+CallLinkStatus CallLinkStatus::computeFor(
+    CodeBlock* profiledBlock, unsigned bytecodeIndex, const ICStatusMap& map)
+{
+    return computeFor(profiledBlock, bytecodeIndex, map, computeExitSiteData(profiledBlock, bytecodeIndex));
+}
+
 CallLinkStatus::ExitSiteData CallLinkStatus::computeExitSiteData(CodeBlock* profiledBlock, unsigned bytecodeIndex)
 {
     ExitSiteData exitSiteData;
@@ -107,11 +112,21 @@ CallLinkStatus::ExitSiteData CallLinkStatus::computeExitSiteData(CodeBlock* prof
     UnlinkedCodeBlock* codeBlock = profiledBlock->unlinkedCodeBlock();
     ConcurrentJSLocker locker(codeBlock->m_lock);
 
-    exitSiteData.takesSlowPath =
-        codeBlock->hasExitSite(locker, DFG::FrequentExitSite(bytecodeIndex, BadType))
-        || codeBlock->hasExitSite(locker, DFG::FrequentExitSite(bytecodeIndex, BadExecutable));
-    exitSiteData.badFunction =
-        codeBlock->hasExitSite(locker, DFG::FrequentExitSite(bytecodeIndex, BadCell));
+    auto takesSlowPath = [&] (ExitingInlineKind inlineKind) -> ExitFlag {
+        return ExitFlag(
+            codeBlock->hasExitSite(locker, DFG::FrequentExitSite(bytecodeIndex, BadType, ExitFromAnything, inlineKind))
+            || codeBlock->hasExitSite(locker, DFG::FrequentExitSite(bytecodeIndex, BadExecutable, ExitFromAnything, inlineKind)),
+            inlineKind);
+    };
+    
+    auto badFunction = [&] (ExitingInlineKind inlineKind) -> ExitFlag {
+        return ExitFlag(codeBlock->hasExitSite(locker, DFG::FrequentExitSite(bytecodeIndex, BadCell, ExitFromAnything, inlineKind)), inlineKind);
+    };
+    
+    exitSiteData.takesSlowPath |= takesSlowPath(ExitFromNotInlined);
+    exitSiteData.takesSlowPath |= takesSlowPath(ExitFromInlined);
+    exitSiteData.badFunction |= badFunction(ExitFromNotInlined);
+    exitSiteData.badFunction |= badFunction(ExitFromInlined);
 #else
     UNUSED_PARAM(profiledBlock);
     UNUSED_PARAM(bytecodeIndex);
@@ -135,7 +150,11 @@ CallLinkStatus CallLinkStatus::computeFor(
 CallLinkStatus CallLinkStatus::computeFromCallLinkInfo(
     const ConcurrentJSLocker&, CallLinkInfo& callLinkInfo)
 {
-    if (callLinkInfo.clearedByGC())
+    // We cannot tell you anything about direct calls.
+    if (callLinkInfo.isDirect())
+        return CallLinkStatus();
+    
+    if (callLinkInfo.clearedByGC() || callLinkInfo.clearedByVirtual())
         return takesSlowPath();
     
     // Note that despite requiring that the locker is held, this code is racy with respect
@@ -158,6 +177,17 @@ CallLinkStatus CallLinkStatus::computeFromCallLinkInfo(
     if (PolymorphicCallStubRoutine* stub = callLinkInfo.stub()) {
         WTF::loadLoadFence();
         
+        if (!stub->hasEdges()) {
+            // This means we have an FTL profile, which has incomplete information.
+            //
+            // It's not clear if takesSlowPath() or CallLinkStatus() are appropriate here, but I
+            // think that takesSlowPath() is a narrow winner.
+            //
+            // Either way, this is telling us that the FTL had been led to believe that it's
+            // profitable not to inline anything.
+            return takesSlowPath();
+        }
+        
         CallEdgeList edges = stub->edges();
         
         // Now that we've loaded the edges list, there are no further concurrency concerns. We will
@@ -227,83 +257,130 @@ CallLinkStatus CallLinkStatus::computeFromCallLinkInfo(
 
 CallLinkStatus CallLinkStatus::computeFor(
     const ConcurrentJSLocker& locker, CodeBlock* profiledBlock, CallLinkInfo& callLinkInfo,
-    ExitSiteData exitSiteData)
+    ExitSiteData exitSiteData, ExitingInlineKind inlineKind)
 {
     CallLinkStatus result = computeFor(locker, profiledBlock, callLinkInfo);
-    if (exitSiteData.badFunction) {
-        if (result.isBasedOnStub()) {
+    result.accountForExits(exitSiteData, inlineKind);
+    return result;
+}
+#endif
+
+void CallLinkStatus::accountForExits(ExitSiteData exitSiteData, ExitingInlineKind inlineKind)
+{
+    if (exitSiteData.badFunction.isSet(inlineKind)) {
+        if (isBasedOnStub()) {
             // If we have a polymorphic stub, then having an exit site is not quite so useful. In
             // most cases, the information in the stub has higher fidelity.
-            result.makeClosureCall();
+            makeClosureCall();
         } else {
             // We might not have a polymorphic stub for any number of reasons. When this happens, we
             // are in less certain territory, so exit sites mean a lot.
-            result.m_couldTakeSlowPath = true;
+            m_couldTakeSlowPath = true;
         }
     }
-    if (exitSiteData.takesSlowPath)
-        result.m_couldTakeSlowPath = true;
     
-    return result;
+    if (exitSiteData.takesSlowPath.isSet(inlineKind))
+        m_couldTakeSlowPath = true;
 }
-#endif
 
-void CallLinkStatus::computeDFGStatuses(
-    CodeBlock* dfgCodeBlock, CallLinkStatus::ContextMap& map)
+CallLinkStatus CallLinkStatus::computeFor(
+    CodeBlock* profiledBlock, CodeOrigin codeOrigin,
+    const ICStatusMap& baselineMap, const ICStatusContextStack& optimizedStack)
 {
-#if ENABLE(DFG_JIT)
-    RELEASE_ASSERT(dfgCodeBlock->jitType() == JITCode::DFGJIT);
-    CodeBlock* baselineCodeBlock = dfgCodeBlock->alternative();
-    for (auto iter = dfgCodeBlock->callLinkInfosBegin(); !!iter; ++iter) {
-        CallLinkInfo& info = **iter;
-        if (info.isDirect()) {
-            // If the DFG was able to get a direct call then probably so will we. However, there is
-            // a remote chance that it's bad news to lose information about what the DFG did. We'd
-            // ideally like to just know that the DFG had emitted a DirectCall.
-            continue;
-        }
-        CodeOrigin codeOrigin = info.codeOrigin();
-        
-        // Check if we had already previously made a terrible mistake in the FTL for this
-        // code origin. Note that this is approximate because we could have a monovariant
-        // inline in the FTL that ended up failing. We should fix that at some point by
-        // having data structures to track the context of frequent exits. This is currently
-        // challenging because it would require creating a CodeOrigin-based database in
-        // baseline CodeBlocks, but those CodeBlocks don't really have a place to put the
-        // InlineCallFrames.
-        CodeBlock* currentBaseline =
-            baselineCodeBlockForOriginAndBaselineCodeBlock(codeOrigin, baselineCodeBlock);
-        ExitSiteData exitSiteData = computeExitSiteData(currentBaseline, codeOrigin.bytecodeIndex);
-        
-        {
-            ConcurrentJSLocker locker(dfgCodeBlock->m_lock);
-            map.add(info.codeOrigin(), computeFor(locker, dfgCodeBlock, info, exitSiteData));
-        }
+    if (CallLinkStatusInternal::verbose)
+        dataLog("Figuring out call profiling for ", codeOrigin, "\n");
+    ExitSiteData exitSiteData = computeExitSiteData(profiledBlock, codeOrigin.bytecodeIndex);
+    if (CallLinkStatusInternal::verbose) {
+        dataLog("takesSlowPath = ", exitSiteData.takesSlowPath, "\n");
+        dataLog("badFunction = ", exitSiteData.badFunction, "\n");
     }
-#else
-    UNUSED_PARAM(dfgCodeBlock);
-#endif // ENABLE(DFG_JIT)
     
-    if (CallLinkStatusInternal::verbose) {
-        dataLog("Context map:\n");
-        ContextMap::iterator iter = map.begin();
-        ContextMap::iterator end = map.end();
-        for (; iter != end; ++iter) {
-            dataLog("    ", iter->key, ":\n");
-            dataLog("        ", iter->value, "\n");
+    for (const ICStatusContext* context : optimizedStack) {
+        if (CallLinkStatusInternal::verbose)
+            dataLog("Examining status in ", pointerDump(context->optimizedCodeBlock), "\n");
+        ICStatus status = context->get(codeOrigin);
+        
+        // If we have both a CallLinkStatus and a callLinkInfo for the same origin, then it means
+        // one of two things:
+        //
+        // 1) We initially thought that we'd be able to inline this call so we recorded a status
+        //    but then we realized that it was pointless and gave up and emitted a normal call
+        //    anyway.
+        //
+        // 2) We did a polymorphic call inline that had a slow path case.
+        //
+        // In the first case, it's essential that we use the callLinkInfo, since the status may
+        // be polymorphic but the link info benefits from polyvariant profiling.
+        //
+        // In the second case, it's essential that we use the status, since the callLinkInfo
+        // corresponds to the slow case.
+        //
+        // It would be annoying to distinguish those two cases. However, we know that:
+        //
+        // If the first case happens in the FTL, then it means that even with polyvariant
+        // profiling, we failed to do anything useful. That suggests that in the FTL, it's OK to
+        // prioritize the status. That status will again tell us to not do anything useful.
+        //
+        // The second case only happens in the FTL.
+        //
+        // Therefore, we prefer the status in the FTL and the info in the DFG.
+        //
+        // Luckily, this case doesn't matter for the other ICStatuses, since they never do the
+        // fast-path-slow-path control-flow-diamond style of IC inlining. It's either all fast
+        // path or it's a full IC. So, for them, if there is an IC status then it means case (1).
+        
+        bool checkStatusFirst = context->optimizedCodeBlock->jitType() == JITCode::FTLJIT;
+        
+        auto bless = [&] (CallLinkStatus& result) {
+            if (!context->isInlined(codeOrigin))
+                result.merge(computeFor(profiledBlock, codeOrigin.bytecodeIndex, baselineMap, exitSiteData));
+        };
+        
+        auto checkInfo = [&] () -> CallLinkStatus {
+            if (!status.callLinkInfo)
+                return CallLinkStatus();
+            
+            if (CallLinkStatusInternal::verbose)
+                dataLog("Have CallLinkInfo with CodeOrigin = ", status.callLinkInfo->codeOrigin(), "\n");
+            CallLinkStatus result;
+            {
+                ConcurrentJSLocker locker(context->optimizedCodeBlock->m_lock);
+                result = computeFor(
+                    locker, context->optimizedCodeBlock, *status.callLinkInfo, exitSiteData,
+                    context->inlineKind(codeOrigin));
+                if (CallLinkStatusInternal::verbose)
+                    dataLog("Got result: ", result, "\n");
+            }
+            bless(result);
+            return result;
+        };
+        
+        auto checkStatus = [&] () -> CallLinkStatus {
+            if (!status.callStatus)
+                return CallLinkStatus();
+            CallLinkStatus result = *status.callStatus;
+            if (CallLinkStatusInternal::verbose)
+                dataLog("Have callStatus: ", result, "\n");
+            result.accountForExits(exitSiteData, context->inlineKind(codeOrigin));
+            bless(result);
+            return result;
+        };
+        
+        if (checkStatusFirst) {
+            if (CallLinkStatus result = checkStatus())
+                return result;
+            if (CallLinkStatus result = checkInfo())
+                return result;
+            continue;
         }
+        
+        if (CallLinkStatus result = checkInfo())
+            return result;
+        if (CallLinkStatus result = checkStatus())
+            return result;
     }
-}
-
-CallLinkStatus CallLinkStatus::computeFor(
-    CodeBlock* profiledBlock, CodeOrigin codeOrigin,
-    const CallLinkInfoMap& baselineMap, const CallLinkStatus::ContextMap& dfgMap)
-{
-    auto iter = dfgMap.find(codeOrigin);
-    if (iter != dfgMap.end())
-        return iter->value;
     
-    return computeFor(profiledBlock, codeOrigin.bytecodeIndex, baselineMap);
+    return computeFor(profiledBlock, codeOrigin.bytecodeIndex, baselineMap, exitSiteData);
 }
 
 void CallLinkStatus::setProvenConstantCallee(CallVariant variant)
@@ -327,6 +404,41 @@ void CallLinkStatus::makeClosureCall()
     m_variants = despecifiedVariantList(m_variants);
 }
 
+bool CallLinkStatus::finalize()
+{
+    for (CallVariant& variant : m_variants) {
+        if (!variant.finalize())
+            return false;
+    }
+    return true;
+}
+
+void CallLinkStatus::merge(const CallLinkStatus& other)
+{
+    m_couldTakeSlowPath |= other.m_couldTakeSlowPath;
+    
+    for (const CallVariant& otherVariant : other.m_variants) {
+        bool found = false;
+        for (CallVariant& thisVariant : m_variants) {
+            if (thisVariant.merge(otherVariant)) {
+                found = true;
+                break;
+            }
+        }
+        if (!found)
+            m_variants.append(otherVariant);
+    }
+}
+
+void CallLinkStatus::filter(VM& vm, JSValue value)
+{
+    m_variants.removeAllMatching(
+        [&] (CallVariant& variant) -> bool {
+            variant.filter(vm, value);
+            return !variant;
+        });
+}
+
 void CallLinkStatus::dump(PrintStream& out) const
 {
     if (!isSet()) {
index 61d0ee7..3a40c92 100644 (file)
@@ -29,6 +29,8 @@
 #include "CallVariant.h"
 #include "CodeOrigin.h"
 #include "ConcurrentJSLock.h"
+#include "ExitFlag.h"
+#include "ICStatusMap.h"
 #include "JSCJSValue.h"
 
 namespace JSC {
@@ -60,39 +62,33 @@ public:
     {
     }
     
-    static CallLinkStatus computeFor(
-        CodeBlock*, unsigned bytecodeIndex, const CallLinkInfoMap&);
-
     struct ExitSiteData {
-        bool takesSlowPath { false };
-        bool badFunction { false };
+        ExitFlag takesSlowPath;
+        ExitFlag badFunction;
     };
     static ExitSiteData computeExitSiteData(CodeBlock*, unsigned bytecodeIndex);
     
+    static CallLinkStatus computeFor(CodeBlock*, unsigned bytecodeIndex, const ICStatusMap&, ExitSiteData);
+    static CallLinkStatus computeFor(CodeBlock*, unsigned bytecodeIndex, const ICStatusMap&);
+
 #if ENABLE(JIT)
     // Computes the status assuming that we never took slow path and never previously
     // exited.
     static CallLinkStatus computeFor(const ConcurrentJSLocker&, CodeBlock*, CallLinkInfo&);
+    
+    // Computes the status accounting for exits.
     static CallLinkStatus computeFor(
-        const ConcurrentJSLocker&, CodeBlock*, CallLinkInfo&, ExitSiteData);
+        const ConcurrentJSLocker&, CodeBlock*, CallLinkInfo&, ExitSiteData, ExitingInlineKind = ExitFromAnyInlineKind);
 #endif
     
-    typedef HashMap<CodeOrigin, CallLinkStatus, CodeOriginApproximateHash> ContextMap;
-    
-    // Computes all of the statuses of the DFG code block. Doesn't include statuses that had
-    // no information. Currently we use this when compiling FTL code, to enable polyvariant
-    // inlining.
-    static void computeDFGStatuses(CodeBlock* dfgCodeBlock, ContextMap&);
-    
-    // Helper that first consults the ContextMap and then does computeFor().
     static CallLinkStatus computeFor(
-        CodeBlock*, CodeOrigin, const CallLinkInfoMap&, const ContextMap&);
+        CodeBlock*, CodeOrigin, const ICStatusMap&, const ICStatusContextStack&);
     
     void setProvenConstantCallee(CallVariant);
     
     bool isSet() const { return !m_variants.isEmpty() || m_couldTakeSlowPath; }
     
-    bool operator!() const { return !isSet(); }
+    explicit operator bool() const { return isSet(); }
     
     bool couldTakeSlowPath() const { return m_couldTakeSlowPath; }
     
@@ -110,6 +106,12 @@ public:
     
     unsigned maxNumArguments() const { return m_maxNumArguments; }
     
+    bool finalize();
+    
+    void merge(const CallLinkStatus&);
+    
+    void filter(VM&, JSValue);
+    
     void dump(PrintStream&) const;
     
 private:
@@ -121,6 +123,8 @@ private:
         const ConcurrentJSLocker&, CallLinkInfo&);
 #endif
     
+    void accountForExits(ExitSiteData, ExitingInlineKind);
+    
     CallVariantList m_variants;
     bool m_couldTakeSlowPath { false };
     bool m_isProved { false };
index 9745dde..3666a13 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014, 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2014-2018 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
 
 namespace JSC {
 
+bool CallVariant::finalize()
+{
+    if (m_callee && !Heap::isMarked(m_callee))
+        return false;
+    return true;
+}
+
+bool CallVariant::merge(const CallVariant& other)
+{
+    if (*this == other)
+        return true;
+    if (executable() == other.executable()) {
+        *this = despecifiedClosure();
+        return true;
+    }
+    return false;
+}
+
+void CallVariant::filter(VM& vm, JSValue value)
+{
+    if (!*this)
+        return;
+    
+    if (!isClosureCall()) {
+        if (nonExecutableCallee() != value)
+            *this = CallVariant();
+        return;
+    }
+    
+    if (JSFunction* function = jsDynamicCast<JSFunction*>(vm, value)) {
+        if (function->executable() == executable())
+            *this = CallVariant(function);
+        else
+            *this = CallVariant();
+        return;
+    }
+    
+    *this = CallVariant();
+}
+
 void CallVariant::dump(PrintStream& out) const
 {
     if (!*this) {
index 61c69e4..c0d06a5 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014, 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2014-2018 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -71,7 +71,7 @@ public:
     {
     }
     
-    bool operator!() const { return !m_callee; }
+    explicit operator bool() const { return !!m_callee; }
     
     // If this variant refers to a function, change it to refer to its executable.
     ALWAYS_INLINE CallVariant despecifiedClosure() const
@@ -136,6 +136,12 @@ public:
         return nullptr;
     }
     
+    bool finalize();
+    
+    bool merge(const CallVariant&);
+    
+    void filter(VM&, JSValue);
+    
     void dump(PrintStream& out) const;
     
     bool isHashTableDeletedValue() const
index 9207e74..d051ab3 100644 (file)
@@ -238,24 +238,20 @@ void CodeBlock::dumpBytecode()
 
 void CodeBlock::dumpBytecode(PrintStream& out)
 {
-    StubInfoMap stubInfos;
-    CallLinkInfoMap callLinkInfos;
-    getStubInfoMap(stubInfos);
-    getCallLinkInfoMap(callLinkInfos);
-    BytecodeDumper<CodeBlock>::dumpBlock(this, instructions(), out, stubInfos, callLinkInfos);
+    ICStatusMap statusMap;
+    getICStatusMap(statusMap);
+    BytecodeDumper<CodeBlock>::dumpBlock(this, instructions(), out, statusMap);
 }
 
-void CodeBlock::dumpBytecode(PrintStream& out, const Instruction* begin, const Instruction*& it, const StubInfoMap& stubInfos, const CallLinkInfoMap& callLinkInfos)
+void CodeBlock::dumpBytecode(PrintStream& out, const Instruction* begin, const Instruction*& it, const ICStatusMap& statusMap)
 {
-    BytecodeDumper<CodeBlock>::dumpBytecode(this, out, begin, it, stubInfos, callLinkInfos);
+    BytecodeDumper<CodeBlock>::dumpBytecode(this, out, begin, it, statusMap);
 }
 
-void CodeBlock::dumpBytecode(
-    PrintStream& out, unsigned bytecodeOffset,
-    const StubInfoMap& stubInfos, const CallLinkInfoMap& callLinkInfos)
+void CodeBlock::dumpBytecode(PrintStream& out, unsigned bytecodeOffset, const ICStatusMap& statusMap)
 {
     const Instruction* it = &instructions()[bytecodeOffset];
-    dumpBytecode(out, instructions().begin(), it, stubInfos, callLinkInfos);
+    dumpBytecode(out, instructions().begin(), it, statusMap);
 }
 
 #define FOR_EACH_MEMBER_VECTOR(macro) \
@@ -1166,6 +1162,9 @@ void CodeBlock::propagateTransitions(const ConcurrentJSLocker&, SlotVisitor& vis
 #if ENABLE(DFG_JIT)
     if (JITCode::isOptimizingJIT(jitType())) {
         DFG::CommonData* dfgCommon = m_jitCode->dfgCommon();
+        
+        dfgCommon->recordedStatuses.markIfCheap(visitor);
+        
         for (auto& weakReference : dfgCommon->weakStructureReferences)
             weakReference->markIfCheap(visitor);
 
@@ -1416,57 +1415,45 @@ void CodeBlock::finalizeUnconditionally(VM&)
         finalizeBaselineJITInlineCaches();
 #endif
 
-    VM::SpaceAndFinalizerSet::finalizerSetFor(*subspace()).remove(this);
-}
-
-void CodeBlock::getStubInfoMap(const ConcurrentJSLocker&, StubInfoMap& result)
-{
-#if ENABLE(JIT)
-    if (JITCode::isJIT(jitType()))
-        toHashMap(m_stubInfos, getStructureStubInfoCodeOrigin, result);
-#else
-    UNUSED_PARAM(result);
-#endif
-}
-
-void CodeBlock::getStubInfoMap(StubInfoMap& result)
-{
-    ConcurrentJSLocker locker(m_lock);
-    getStubInfoMap(locker, result);
-}
-
-void CodeBlock::getCallLinkInfoMap(const ConcurrentJSLocker&, CallLinkInfoMap& result)
-{
-#if ENABLE(JIT)
-    if (JITCode::isJIT(jitType()))
-        toHashMap(m_callLinkInfos, getCallLinkInfoCodeOrigin, result);
-#else
-    UNUSED_PARAM(result);
-#endif
-}
+    if (JITCode::isOptimizingJIT(jitType())) {
+        DFG::CommonData* dfgCommon = m_jitCode->dfgCommon();
+        dfgCommon->recordedStatuses.finalize();
+    }
 
-void CodeBlock::getCallLinkInfoMap(CallLinkInfoMap& result)
-{
-    ConcurrentJSLocker locker(m_lock);
-    getCallLinkInfoMap(locker, result);
+    VM::SpaceAndFinalizerSet::finalizerSetFor(*subspace()).remove(this);
 }
 
-void CodeBlock::getByValInfoMap(const ConcurrentJSLocker&, ByValInfoMap& result)
+void CodeBlock::getICStatusMap(const ConcurrentJSLocker&, ICStatusMap& result)
 {
 #if ENABLE(JIT)
     if (JITCode::isJIT(jitType())) {
-        for (auto* byValInfo : m_byValInfos)
-            result.add(CodeOrigin(byValInfo->bytecodeIndex), byValInfo);
+        for (StructureStubInfo* stubInfo : m_stubInfos)
+            result.add(stubInfo->codeOrigin, ICStatus()).iterator->value.stubInfo = stubInfo;
+        for (CallLinkInfo* callLinkInfo : m_callLinkInfos)
+            result.add(callLinkInfo->codeOrigin(), ICStatus()).iterator->value.callLinkInfo = callLinkInfo;
+        for (ByValInfo* byValInfo : m_byValInfos)
+            result.add(CodeOrigin(byValInfo->bytecodeIndex), ICStatus()).iterator->value.byValInfo = byValInfo;
+        if (JITCode::isOptimizingJIT(jitType())) {
+            DFG::CommonData* dfgCommon = m_jitCode->dfgCommon();
+            for (auto& pair : dfgCommon->recordedStatuses.calls)
+                result.add(pair.first, ICStatus()).iterator->value.callStatus = pair.second.get();
+            for (auto& pair : dfgCommon->recordedStatuses.gets)
+                result.add(pair.first, ICStatus()).iterator->value.getStatus = pair.second.get();
+            for (auto& pair : dfgCommon->recordedStatuses.puts)
+                result.add(pair.first, ICStatus()).iterator->value.putStatus = pair.second.get();
+            for (auto& pair : dfgCommon->recordedStatuses.ins)
+                result.add(pair.first, ICStatus()).iterator->value.inStatus = pair.second.get();
+        }
     }
 #else
     UNUSED_PARAM(result);
 #endif
 }
 
-void CodeBlock::getByValInfoMap(ByValInfoMap& result)
+void CodeBlock::getICStatusMap(ICStatusMap& result)
 {
     ConcurrentJSLocker locker(m_lock);
-    getByValInfoMap(locker, result);
+    getICStatusMap(locker, result);
 }
 
 #if ENABLE(JIT)
@@ -1533,7 +1520,7 @@ void CodeBlock::resetJITData()
     
     // We can clear these because no other thread will have references to any stub infos, call
     // link infos, or by val infos if we don't have JIT code. Attempts to query these data
-    // structures using the concurrent API (getStubInfoMap and friends) will return nothing if we
+    // structures using the concurrent API (getICStatusMap and friends) will return nothing if we
     // don't have JIT code.
     m_stubInfos.clear();
     m_callLinkInfos.clear();
index 667bd6a..a3a3d26 100644 (file)
@@ -45,6 +45,7 @@
 #include "ExpressionRangeInfo.h"
 #include "FunctionExecutable.h"
 #include "HandlerInfo.h"
+#include "ICStatusMap.h"
 #include "Instruction.h"
 #include "JITCode.h"
 #include "JITCodeMap.h"
@@ -96,8 +97,6 @@ enum class AccessType : int8_t;
 
 struct ArithProfile;
 
-typedef HashMap<CodeOrigin, StructureStubInfo*, CodeOriginApproximateHash> StubInfoMap;
-
 enum ReoptimizationMode { DontCountReoptimization, CountReoptimization };
 
 class CodeBlock : public JSCell {
@@ -198,8 +197,8 @@ public:
 
     void dumpBytecode();
     void dumpBytecode(PrintStream&);
-    void dumpBytecode(PrintStream& out, const Instruction* begin, const Instruction*& it, const StubInfoMap& = StubInfoMap(), const CallLinkInfoMap& = CallLinkInfoMap());
-    void dumpBytecode(PrintStream& out, unsigned bytecodeOffset, const StubInfoMap& = StubInfoMap(), const CallLinkInfoMap& = CallLinkInfoMap());
+    void dumpBytecode(PrintStream& out, const Instruction* begin, const Instruction*& it, const ICStatusMap& = ICStatusMap());
+    void dumpBytecode(PrintStream& out, unsigned bytecodeOffset, const ICStatusMap& = ICStatusMap());
 
     void dumpExceptionHandlers(PrintStream&);
     void printStructures(PrintStream&, const Instruction*);
@@ -239,14 +238,8 @@ public:
 
     std::optional<unsigned> bytecodeOffsetFromCallSiteIndex(CallSiteIndex);
 
-    void getStubInfoMap(const ConcurrentJSLocker&, StubInfoMap& result);
-    void getStubInfoMap(StubInfoMap& result);
-    
-    void getCallLinkInfoMap(const ConcurrentJSLocker&, CallLinkInfoMap& result);
-    void getCallLinkInfoMap(CallLinkInfoMap& result);
-
-    void getByValInfoMap(const ConcurrentJSLocker&, ByValInfoMap& result);
-    void getByValInfoMap(ByValInfoMap& result);
+    void getICStatusMap(const ConcurrentJSLocker&, ICStatusMap& result);
+    void getICStatusMap(ICStatusMap& result);
     
 #if ENABLE(JIT)
     JITAddIC* addJITAddIC(ArithProfile*, Instruction*);
index a52df92..05e79da 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012-2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2012-2018 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -46,7 +46,7 @@ unsigned CodeOrigin::inlineDepth() const
     return inlineDepthForCallFrame(inlineCallFrame);
 }
 
-bool CodeOrigin::isApproximatelyEqualTo(const CodeOrigin& other) const
+bool CodeOrigin::isApproximatelyEqualTo(const CodeOrigin& other, InlineCallFrame* terminal) const
 {
     CodeOrigin a = *this;
     CodeOrigin b = other;
@@ -68,10 +68,12 @@ bool CodeOrigin::isApproximatelyEqualTo(const CodeOrigin& other) const
         if (a.bytecodeIndex != b.bytecodeIndex)
             return false;
         
-        if ((!!a.inlineCallFrame) != (!!b.inlineCallFrame))
+        bool aHasInlineCallFrame = !!a.inlineCallFrame && a.inlineCallFrame != terminal;
+        bool bHasInlineCallFrame = !!b.inlineCallFrame;
+        if (aHasInlineCallFrame != bHasInlineCallFrame)
             return false;
         
-        if (!a.inlineCallFrame)
+        if (!aHasInlineCallFrame)
             return true;
         
         if (a.inlineCallFrame->baselineCodeBlock.get() != b.inlineCallFrame->baselineCodeBlock.get())
@@ -82,7 +84,7 @@ bool CodeOrigin::isApproximatelyEqualTo(const CodeOrigin& other) const
     }
 }
 
-unsigned CodeOrigin::approximateHash() const
+unsigned CodeOrigin::approximateHash(InlineCallFrame* terminal) const
 {
     if (!isSet())
         return 0;
@@ -97,6 +99,9 @@ unsigned CodeOrigin::approximateHash() const
         if (!codeOrigin.inlineCallFrame)
             return result;
         
+        if (codeOrigin.inlineCallFrame == terminal)
+            return result;
+        
         result += WTF::PtrHash<JSCell*>::hash(codeOrigin.inlineCallFrame->baselineCodeBlock.get());
         
         codeOrigin = codeOrigin.inlineCallFrame->directCaller;
index 0c05b96..19d9cd0 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011-2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2011-2018 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -26,6 +26,7 @@
 #pragma once
 
 #include "CodeBlockHash.h"
+#include "ExitingInlineKind.h"
 #include <limits.h>
 #include <wtf/HashMap.h>
 #include <wtf/PrintStream.h>
@@ -88,15 +89,20 @@ struct CodeOrigin {
     
     static unsigned inlineDepthForCallFrame(InlineCallFrame*);
     
+    ExitingInlineKind exitingInlineKind() const
+    {
+        return inlineCallFrame ? ExitFromInlined : ExitFromNotInlined;
+    }
+    
     unsigned hash() const;
     bool operator==(const CodeOrigin& other) const;
     bool operator!=(const CodeOrigin& other) const { return !(*this == other); }
     
     // This checks if the two code origins correspond to the same stack trace snippets,
     // but ignore whether the InlineCallFrame's are identical.
-    bool isApproximatelyEqualTo(const CodeOrigin& other) const;
+    bool isApproximatelyEqualTo(const CodeOrigin& other, InlineCallFrame* terminal = nullptr) const;
     
-    unsigned approximateHash() const;
+    unsigned approximateHash(InlineCallFrame* terminal = nullptr) const;
 
     template <typename Function>
     void walkUpInlineStack(const Function&);
@@ -106,7 +112,7 @@ struct CodeOrigin {
     
     JS_EXPORT_PRIVATE void dump(PrintStream&) const;
     void dumpInContext(PrintStream&, DumpContext*) const;
-
+    
 private:
     static InlineCallFrame* deletedMarker()
     {
index 9aa536a..2d9431c 100644 (file)
@@ -35,7 +35,7 @@ namespace JSC { namespace DFG {
 
 void FrequentExitSite::dump(PrintStream& out) const
 {
-    out.print("bc#", m_bytecodeOffset, ": ", m_kind, "/", m_jitType);
+    out.print("bc#", m_bytecodeOffset, ": ", m_kind, "/", m_jitType, "/", m_inlineKind);
 }
 
 ExitProfile::ExitProfile() { }
@@ -43,8 +43,10 @@ ExitProfile::~ExitProfile() { }
 
 bool ExitProfile::add(CodeBlock* owner, const FrequentExitSite& site)
 {
+    RELEASE_ASSERT(site.jitType() != ExitFromAnything);
+    RELEASE_ASSERT(site.inlineKind() != ExitFromAnyInlineKind);
+
     ConcurrentJSLocker locker(owner->unlinkedCodeBlock()->m_lock);
-    ASSERT(site.jitType() != ExitFromAnything);
 
     CODEBLOCK_LOG_EVENT(owner, "frequentExit", (site));
     
index be18487..4b0d941 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011-2014, 2016 Apple Inc. All rights reserved.
+ * Copyright (C) 2011-2018 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -29,6 +29,7 @@
 
 #include "ConcurrentJSLock.h"
 #include "ExitKind.h"
+#include "ExitingInlineKind.h"
 #include "ExitingJITType.h"
 #include <wtf/HashSet.h>
 #include <wtf/Vector.h>
@@ -41,6 +42,7 @@ public:
         : m_bytecodeOffset(0) // 0 = empty value
         , m_kind(ExitKindUnset)
         , m_jitType(ExitFromAnything)
+        , m_inlineKind(ExitFromAnyInlineKind)
     {
     }
     
@@ -48,13 +50,15 @@ public:
         : m_bytecodeOffset(1) // 1 = deleted value
         , m_kind(ExitKindUnset)
         , m_jitType(ExitFromAnything)
+        , m_inlineKind(ExitFromAnyInlineKind)
     {
     }
     
-    explicit FrequentExitSite(unsigned bytecodeOffset, ExitKind kind, ExitingJITType jitType = ExitFromAnything)
+    explicit FrequentExitSite(unsigned bytecodeOffset, ExitKind kind, ExitingJITType jitType = ExitFromAnything, ExitingInlineKind inlineKind = ExitFromAnyInlineKind)
         : m_bytecodeOffset(bytecodeOffset)
         , m_kind(kind)
         , m_jitType(jitType)
+        , m_inlineKind(inlineKind)
     {
         if (m_kind == ArgumentsEscaped) {
             // Count this one globally. It doesn't matter where in the code block the arguments excaped;
@@ -65,10 +69,11 @@ public:
     
     // Use this constructor if you wish for the exit site to be counted globally within its
     // code block.
-    explicit FrequentExitSite(ExitKind kind, ExitingJITType jitType = ExitFromAnything)
+    explicit FrequentExitSite(ExitKind kind, ExitingJITType jitType = ExitFromAnything, ExitingInlineKind inlineKind = ExitFromAnyInlineKind)
         : m_bytecodeOffset(0)
         , m_kind(kind)
         , m_jitType(jitType)
+        , m_inlineKind(inlineKind)
     {
     }
     
@@ -81,7 +86,8 @@ public:
     {
         return m_bytecodeOffset == other.m_bytecodeOffset
             && m_kind == other.m_kind
-            && m_jitType == other.m_jitType;
+            && m_jitType == other.m_jitType
+            && m_inlineKind == other.m_inlineKind;
     }
     
     bool subsumes(const FrequentExitSite& other) const
@@ -90,19 +96,24 @@ public:
             return false;
         if (m_kind != other.m_kind)
             return false;
-        if (m_jitType == ExitFromAnything)
-            return true;
-        return m_jitType == other.m_jitType;
+        if (m_jitType != ExitFromAnything
+            && m_jitType != other.m_jitType)
+            return false;
+        if (m_inlineKind != ExitFromAnyInlineKind
+            && m_inlineKind != other.m_inlineKind)
+            return false;
+        return true;
     }
     
     unsigned hash() const
     {
-        return WTF::intHash(m_bytecodeOffset) + m_kind + static_cast<std::underlying_type_t<ExitingJITType>>(m_jitType) * 7;
+        return WTF::intHash(m_bytecodeOffset) + m_kind + static_cast<unsigned>(m_jitType) * 7 + static_cast<unsigned>(m_inlineKind) * 11;
     }
     
     unsigned bytecodeOffset() const { return m_bytecodeOffset; }
     ExitKind kind() const { return m_kind; }
     ExitingJITType jitType() const { return m_jitType; }
+    ExitingInlineKind inlineKind() const { return m_inlineKind; }
     
     FrequentExitSite withJITType(ExitingJITType jitType) const
     {
@@ -111,6 +122,13 @@ public:
         return result;
     }
 
+    FrequentExitSite withInlineKind(ExitingInlineKind inlineKind) const
+    {
+        FrequentExitSite result = *this;
+        result.m_inlineKind = inlineKind;
+        return result;
+    }
+
     bool isHashTableDeletedValue() const
     {
         return m_kind == ExitKindUnset && m_bytecodeOffset;
@@ -122,6 +140,7 @@ private:
     unsigned m_bytecodeOffset;
     ExitKind m_kind;
     ExitingJITType m_jitType;
+    ExitingInlineKind m_inlineKind;
 };
 
 struct FrequentExitSiteHash {
@@ -192,10 +211,10 @@ public:
     bool hasExitSite(const FrequentExitSite& site) const
     {
         if (site.jitType() == ExitFromAnything) {
-            return hasExitSite(site.withJITType(ExitFromDFG))
-                || hasExitSite(site.withJITType(ExitFromFTL));
+            return hasExitSiteWithSpecificJITType(site.withJITType(ExitFromDFG))
+                || hasExitSiteWithSpecificJITType(site.withJITType(ExitFromFTL));
         }
-        return m_frequentExitSites.find(site) != m_frequentExitSites.end();
+        return hasExitSiteWithSpecificJITType(site);
     }
     
     bool hasExitSite(ExitKind kind) const
@@ -208,6 +227,20 @@ public:
         return hasExitSite(FrequentExitSite(bytecodeIndex, kind));
     }
 private:
+    bool hasExitSiteWithSpecificJITType(const FrequentExitSite& site) const
+    {
+        if (site.inlineKind() == ExitFromAnyInlineKind) {
+            return hasExitSiteWithSpecificInlineKind(site.withInlineKind(ExitFromNotInlined))
+                || hasExitSiteWithSpecificInlineKind(site.withInlineKind(ExitFromInlined));
+        }
+        return hasExitSiteWithSpecificInlineKind(site);
+    }
+    
+    bool hasExitSiteWithSpecificInlineKind(const FrequentExitSite& site) const
+    {
+        return m_frequentExitSites.find(site) != m_frequentExitSites.end();
+    }
+    
     HashSet<FrequentExitSite> m_frequentExitSites;
 };
 
diff --git a/Source/JavaScriptCore/bytecode/ExitFlag.cpp b/Source/JavaScriptCore/bytecode/ExitFlag.cpp
new file mode 100644 (file)
index 0000000..7551178
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2018 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 "ExitFlag.h"
+
+#include <wtf/CommaPrinter.h>
+
+namespace JSC {
+
+void ExitFlag::dump(PrintStream& out) const
+{
+    if (!m_bits) {
+        out.print("false");
+        return;
+    }
+    
+    CommaPrinter comma("|");
+    if (isSet(ExitFromNotInlined))
+        out.print(comma, "notInlined");
+    if (isSet(ExitFromInlined))
+        out.print(comma, "inlined");
+}
+
+} // namespace JSC
+
diff --git a/Source/JavaScriptCore/bytecode/ExitFlag.h b/Source/JavaScriptCore/bytecode/ExitFlag.h
new file mode 100644 (file)
index 0000000..5a3d408
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2018 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. 
+ */
+
+#pragma once
+
+#include "ExitingInlineKind.h"
+
+namespace JSC {
+
+class ExitFlag {
+public:
+    ExitFlag() { }
+    
+    ExitFlag(bool value, ExitingInlineKind inlineKind)
+    {
+        if (!value)
+            return;
+        
+        switch (inlineKind) {
+        case ExitFromAnyInlineKind:
+            m_bits = trueNotInlined | trueInlined;
+            break;
+        case ExitFromNotInlined:
+            m_bits = trueNotInlined;
+            break;
+        case ExitFromInlined:
+            m_bits = trueInlined;
+            break;
+        }
+    }
+    
+    ExitFlag operator|(const ExitFlag& other) const
+    {
+        ExitFlag result;
+        result.m_bits = m_bits | other.m_bits;
+        return result;
+    }
+    
+    ExitFlag& operator|=(const ExitFlag& other)
+    {
+        *this = *this | other;
+        return *this;
+    }
+    
+    ExitFlag operator&(const ExitFlag& other) const
+    {
+        ExitFlag result;
+        result.m_bits = m_bits & other.m_bits;
+        return result;
+    }
+    
+    ExitFlag& operator&=(const ExitFlag& other)
+    {
+        *this = *this & other;
+        return *this;
+    }
+    
+    explicit operator bool() const
+    {
+        return !!m_bits;
+    }
+    
+    bool isSet(ExitingInlineKind inlineKind) const
+    {
+        return !!(*this & ExitFlag(true, inlineKind));
+    }
+    
+    void dump(PrintStream&) const;
+    
+private:
+    static constexpr uint8_t trueNotInlined = 1;
+    static constexpr uint8_t trueInlined = 2;
+    
+    uint8_t m_bits { 0 };
+};
+
+} // namespace JSC
+
diff --git a/Source/JavaScriptCore/bytecode/ExitingInlineKind.cpp b/Source/JavaScriptCore/bytecode/ExitingInlineKind.cpp
new file mode 100644 (file)
index 0000000..4dd08a6
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2018 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 "ExitingInlineKind.h"
+
+#include <wtf/PrintStream.h>
+
+namespace WTF {
+
+using namespace JSC;
+
+void printInternal(PrintStream& out, ExitingInlineKind type)
+{
+    switch (type) {
+    case ExitFromAnyInlineKind:
+        out.print("FromAnyInlineKind");
+        return;
+    case ExitFromNotInlined:
+        out.print("FromNotInlined");
+        return;
+    case ExitFromInlined:
+        out.print("FromInlined");
+        return;
+    }
+    RELEASE_ASSERT_NOT_REACHED();
+}
+
+} // namespace WTF
+
diff --git a/Source/JavaScriptCore/bytecode/ExitingInlineKind.h b/Source/JavaScriptCore/bytecode/ExitingInlineKind.h
new file mode 100644 (file)
index 0000000..e9122de
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2018 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. 
+ */
+
+#pragma once
+
+namespace JSC {
+
+enum ExitingInlineKind : uint8_t {
+    ExitFromAnyInlineKind,
+    ExitFromNotInlined,
+    ExitFromInlined,
+};
+
+} // namespace JSC
+
+namespace WTF {
+
+class PrintStream;
+void printInternal(PrintStream&, JSC::ExitingInlineKind);
+
+} // namespace WTF
+
+
+
index 972d5de..b0946c4 100644 (file)
@@ -51,16 +51,6 @@ bool GetByIdStatus::appendVariant(const GetByIdVariant& variant)
     return appendICStatusVariant(m_variants, variant);
 }
 
-#if ENABLE(DFG_JIT)
-bool GetByIdStatus::hasExitSite(CodeBlock* profiledBlock, unsigned bytecodeIndex)
-{
-    UnlinkedCodeBlock* unlinkedCodeBlock = profiledBlock->unlinkedCodeBlock();
-    ConcurrentJSLocker locker(unlinkedCodeBlock->m_lock);
-    return unlinkedCodeBlock->hasExitSite(locker, DFG::FrequentExitSite(bytecodeIndex, BadCache))
-        || unlinkedCodeBlock->hasExitSite(locker, DFG::FrequentExitSite(bytecodeIndex, BadConstantCache));
-}
-#endif
-
 GetByIdStatus GetByIdStatus::computeFromLLInt(CodeBlock* profiledBlock, unsigned bytecodeIndex, UniquedStringImpl* uid)
 {
     VM& vm = *profiledBlock->vm();
@@ -105,7 +95,7 @@ GetByIdStatus GetByIdStatus::computeFromLLInt(CodeBlock* profiledBlock, unsigned
     }
 }
 
-GetByIdStatus GetByIdStatus::computeFor(CodeBlock* profiledBlock, StubInfoMap& map, unsigned bytecodeIndex, UniquedStringImpl* uid)
+GetByIdStatus GetByIdStatus::computeFor(CodeBlock* profiledBlock, ICStatusMap& map, unsigned bytecodeIndex, UniquedStringImpl* uid, ExitFlag didExit, CallLinkStatus::ExitSiteData callExitSiteData)
 {
     ConcurrentJSLocker locker(profiledBlock->m_lock);
 
@@ -113,12 +103,11 @@ GetByIdStatus GetByIdStatus::computeFor(CodeBlock* profiledBlock, StubInfoMap& m
 
 #if ENABLE(DFG_JIT)
     result = computeForStubInfoWithoutExitSiteFeedback(
-        locker, profiledBlock, map.get(CodeOrigin(bytecodeIndex)), uid,
-        CallLinkStatus::computeExitSiteData(profiledBlock, bytecodeIndex));
+        locker, profiledBlock, map.get(CodeOrigin(bytecodeIndex)).stubInfo, uid,
+        callExitSiteData);
     
-    if (!result.takesSlowPath()
-        && hasExitSite(profiledBlock, bytecodeIndex))
-        return GetByIdStatus(result.makesCalls() ? MakesCalls : TakesSlowPath, true);
+    if (didExit)
+        return result.slowVersion();
 #else
     UNUSED_PARAM(map);
 #endif
@@ -136,8 +125,8 @@ GetByIdStatus GetByIdStatus::computeForStubInfo(const ConcurrentJSLocker& locker
         locker, profiledBlock, stubInfo, uid,
         CallLinkStatus::computeExitSiteData(profiledBlock, codeOrigin.bytecodeIndex));
 
-    if (!result.takesSlowPath() && GetByIdStatus::hasExitSite(profiledBlock, codeOrigin.bytecodeIndex))
-        return GetByIdStatus(result.makesCalls() ? GetByIdStatus::MakesCalls : GetByIdStatus::TakesSlowPath, true);
+    if (!result.takesSlowPath() && hasBadCacheExitSite(profiledBlock, codeOrigin.bytecodeIndex))
+        return result.slowVersion();
     return result;
 }
 #endif // ENABLE(DFG_JIT)
@@ -156,22 +145,9 @@ GetByIdStatus GetByIdStatus::computeForStubInfoWithoutExitSiteFeedback(
     const ConcurrentJSLocker& locker, CodeBlock* profiledBlock, StructureStubInfo* stubInfo, UniquedStringImpl* uid,
     CallLinkStatus::ExitSiteData callExitSiteData)
 {
-    if (!stubInfo || !stubInfo->everConsidered)
-        return GetByIdStatus(NoInformation);
-
-    PolymorphicAccess* list = 0;
-    State slowPathState = TakesSlowPath;
-    if (stubInfo->cacheType == CacheType::Stub) {
-        list = stubInfo->u.stub;
-        for (unsigned i = 0; i < list->size(); ++i) {
-            const AccessCase& access = list->at(i);
-            if (access.doesCalls())
-                slowPathState = MakesCalls;
-        }
-    }
-    
-    if (stubInfo->tookSlowPath)
-        return GetByIdStatus(slowPathState);
+    StubInfoSummary summary = StructureStubInfo::summary(stubInfo);
+    if (!isInlineable(summary))
+        return GetByIdStatus(summary);
     
     // Finally figure out if we can derive an access strategy.
     GetByIdStatus result;
@@ -184,14 +160,14 @@ GetByIdStatus GetByIdStatus::computeForStubInfoWithoutExitSiteFeedback(
     case CacheType::GetByIdSelf: {
         Structure* structure = stubInfo->u.byIdSelf.baseObjectStructure.get();
         if (structure->takesSlowPathInDFGForImpureProperty())
-            return GetByIdStatus(slowPathState, true);
+            return GetByIdStatus(JSC::slowVersion(summary));
         unsigned attributes;
         GetByIdVariant variant;
         variant.m_offset = structure->getConcurrently(uid, attributes);
         if (!isValidOffset(variant.m_offset))
-            return GetByIdStatus(slowPathState, true);
+            return GetByIdStatus(JSC::slowVersion(summary));
         if (attributes & PropertyAttribute::CustomAccessor)
-            return GetByIdStatus(slowPathState, true);
+            return GetByIdStatus(JSC::slowVersion(summary));
         
         variant.m_structureSet.add(structure);
         bool didAppend = result.appendVariant(variant);
@@ -200,6 +176,7 @@ GetByIdStatus GetByIdStatus::computeForStubInfoWithoutExitSiteFeedback(
     }
         
     case CacheType::Stub: {
+        PolymorphicAccess* list = stubInfo->u.stub;
         if (list->size() == 1) {
             const AccessCase& access = list->at(0);
             switch (access.type()) {
@@ -213,10 +190,10 @@ GetByIdStatus GetByIdStatus::computeForStubInfoWithoutExitSiteFeedback(
         for (unsigned listIndex = 0; listIndex < list->size(); ++listIndex) {
             const AccessCase& access = list->at(listIndex);
             if (access.viaProxy())
-                return GetByIdStatus(slowPathState, true);
+                return GetByIdStatus(JSC::slowVersion(summary));
 
             if (access.usesPolyProto())
-                return GetByIdStatus(slowPathState, true);
+                return GetByIdStatus(JSC::slowVersion(summary));
             
             Structure* structure = access.structure();
             if (!structure) {
@@ -226,7 +203,7 @@ GetByIdStatus GetByIdStatus::computeForStubInfoWithoutExitSiteFeedback(
                 // shouldn't have to use value profiling to discover something that the AccessCase
                 // could have told us. But, it works well enough. So, our only concern here is to not
                 // crash on null structure.
-                return GetByIdStatus(slowPathState, true);
+                return GetByIdStatus(JSC::slowVersion(summary));
             }
             
             ComplexGetStatus complexGetStatus = ComplexGetStatus::computeFor(
@@ -237,7 +214,7 @@ GetByIdStatus GetByIdStatus::computeForStubInfoWithoutExitSiteFeedback(
                 continue;
                  
             case ComplexGetStatus::TakesSlowPath:
-                return GetByIdStatus(slowPathState, true);
+                return GetByIdStatus(JSC::slowVersion(summary));
                  
             case ComplexGetStatus::Inlineable: {
                 std::unique_ptr<CallLinkStatus> callLinkStatus;
@@ -267,14 +244,14 @@ GetByIdStatus GetByIdStatus::computeForStubInfoWithoutExitSiteFeedback(
                     customAccessorGetter = access.as<GetterSetterAccessCase>().customAccessor();
                     domAttribute = access.as<GetterSetterAccessCase>().domAttribute();
                     if (!domAttribute)
-                        return GetByIdStatus(slowPathState, true);
+                        return GetByIdStatus(JSC::slowVersion(summary));
                     result.m_state = Custom;
                     break;
                 }
                 default: {
                     // FIXME: It would be totally sweet to support more of these at some point in the
                     // future. https://bugs.webkit.org/show_bug.cgi?id=133052
-                    return GetByIdStatus(slowPathState, true);
+                    return GetByIdStatus(JSC::slowVersion(summary));
                 } }
 
                 ASSERT((AccessCase::Miss == access.type()) == (access.offset() == invalidOffset));
@@ -286,16 +263,16 @@ GetByIdStatus GetByIdStatus::computeForStubInfoWithoutExitSiteFeedback(
                     domAttribute);
 
                 if (!result.appendVariant(variant))
-                    return GetByIdStatus(slowPathState, true);
+                    return GetByIdStatus(JSC::slowVersion(summary));
 
                 if (domAttribute) {
                     // Give up when cutom accesses are not merged into one.
                     if (result.numVariants() != 1)
-                        return GetByIdStatus(slowPathState, true);
+                        return GetByIdStatus(JSC::slowVersion(summary));
                 } else {
                     // Give up when custom access and simple access are mixed.
                     if (result.m_state == Custom)
-                        return GetByIdStatus(slowPathState, true);
+                        return GetByIdStatus(JSC::slowVersion(summary));
                 }
                 break;
             } }
@@ -305,7 +282,7 @@ GetByIdStatus GetByIdStatus::computeForStubInfoWithoutExitSiteFeedback(
     }
         
     default:
-        return GetByIdStatus(slowPathState, true);
+        return GetByIdStatus(JSC::slowVersion(summary));
     }
     
     RELEASE_ASSERT_NOT_REACHED();
@@ -314,40 +291,47 @@ GetByIdStatus GetByIdStatus::computeForStubInfoWithoutExitSiteFeedback(
 #endif // ENABLE(JIT)
 
 GetByIdStatus GetByIdStatus::computeFor(
-    CodeBlock* profiledBlock, CodeBlock* dfgBlock, StubInfoMap& baselineMap,
-    StubInfoMap& dfgMap, CodeOrigin codeOrigin, UniquedStringImpl* uid)
+    CodeBlock* profiledBlock, ICStatusMap& baselineMap,
+    ICStatusContextStack& icContextStack, CodeOrigin codeOrigin, UniquedStringImpl* uid)
 {
-#if ENABLE(DFG_JIT)
-    if (dfgBlock) {
-        CallLinkStatus::ExitSiteData exitSiteData;
-        {
-            ConcurrentJSLocker locker(profiledBlock->m_lock);
-            exitSiteData = CallLinkStatus::computeExitSiteData(
-                profiledBlock, codeOrigin.bytecodeIndex);
-        }
-        
-        GetByIdStatus result;
-        {
-            ConcurrentJSLocker locker(dfgBlock->m_lock);
-            result = computeForStubInfoWithoutExitSiteFeedback(
-                locker, dfgBlock, dfgMap.get(codeOrigin), uid, exitSiteData);
-        }
-
-        if (result.takesSlowPath())
-            return result;
+    CallLinkStatus::ExitSiteData callExitSiteData =
+        CallLinkStatus::computeExitSiteData(profiledBlock, codeOrigin.bytecodeIndex);
+    ExitFlag didExit = hasBadCacheExitSite(profiledBlock, codeOrigin.bytecodeIndex);
     
-        if (hasExitSite(profiledBlock, codeOrigin.bytecodeIndex))
-            return GetByIdStatus(TakesSlowPath, true);
+    for (ICStatusContext* context : icContextStack) {
+        ICStatus status = context->get(codeOrigin);
         
-        if (result.isSet())
+        auto bless = [&] (const GetByIdStatus& result) -> GetByIdStatus {
+            if (!context->isInlined(codeOrigin)) {
+                // Merge with baseline result, which also happens to contain exit data for both
+                // inlined and not-inlined.
+                GetByIdStatus baselineResult = computeFor(
+                    profiledBlock, baselineMap, codeOrigin.bytecodeIndex, uid, didExit,
+                    callExitSiteData);
+                baselineResult.merge(result);
+                return baselineResult;
+            }
+            if (didExit.isSet(ExitFromInlined))
+                return result.slowVersion();
             return result;
+        };
+        
+        if (status.stubInfo) {
+            GetByIdStatus result;
+            {
+                ConcurrentJSLocker locker(context->optimizedCodeBlock->m_lock);
+                result = computeForStubInfoWithoutExitSiteFeedback(
+                    locker, context->optimizedCodeBlock, status.stubInfo, uid, callExitSiteData);
+            }
+            if (result.isSet())
+                return bless(result);
+        }
+        
+        if (status.getStatus)
+            return bless(*status.getStatus);
     }
-#else
-    UNUSED_PARAM(dfgBlock);
-    UNUSED_PARAM(dfgMap);
-#endif
-
-    return computeFor(profiledBlock, baselineMap, codeOrigin.bytecodeIndex, uid);
+    
+    return computeFor(profiledBlock, baselineMap, codeOrigin.bytecodeIndex, uid, didExit, callExitSiteData);
 }
 
 GetByIdStatus GetByIdStatus::computeFor(const StructureSet& set, UniquedStringImpl* uid)
@@ -414,6 +398,59 @@ bool GetByIdStatus::makesCalls() const
     return false;
 }
 
+GetByIdStatus GetByIdStatus::slowVersion() const
+{
+    return GetByIdStatus(makesCalls() ? MakesCalls : TakesSlowPath, wasSeenInJIT());
+}
+
+void GetByIdStatus::merge(const GetByIdStatus& other)
+{
+    if (other.m_state == NoInformation)
+        return;
+    
+    auto mergeSlow = [&] () {
+        *this = GetByIdStatus((makesCalls() || other.makesCalls()) ? MakesCalls : TakesSlowPath);
+    };
+    
+    switch (m_state) {
+    case NoInformation:
+        *this = other;
+        return;
+        
+    case Simple:
+    case Custom:
+        if (m_state != other.m_state)
+            return mergeSlow();
+        
+        for (const GetByIdVariant& otherVariant : other.m_variants) {
+            if (!appendVariant(otherVariant))
+                return mergeSlow();
+        }
+        return;
+        
+    case ModuleNamespace:
+        if (other.m_state != ModuleNamespace)
+            return mergeSlow();
+        
+        if (m_moduleNamespaceObject != other.m_moduleNamespaceObject)
+            return mergeSlow();
+        
+        if (m_moduleEnvironment != other.m_moduleEnvironment)
+            return mergeSlow();
+        
+        if (m_scopeOffset != other.m_scopeOffset)
+            return mergeSlow();
+        
+        return;
+        
+    case TakesSlowPath:
+    case MakesCalls:
+        return mergeSlow();
+    }
+    
+    RELEASE_ASSERT_NOT_REACHED();
+}
+
 void GetByIdStatus::filter(const StructureSet& set)
 {
     if (m_state != Simple)
@@ -423,6 +460,25 @@ void GetByIdStatus::filter(const StructureSet& set)
         m_state = NoInformation;
 }
 
+void GetByIdStatus::markIfCheap(SlotVisitor& visitor)
+{
+    for (GetByIdVariant& variant : m_variants)
+        variant.markIfCheap(visitor);
+}
+
+bool GetByIdStatus::finalize()
+{
+    for (GetByIdVariant& variant : m_variants) {
+        if (!variant.finalize())
+            return false;
+    }
+    if (m_moduleNamespaceObject && !Heap::isMarked(m_moduleNamespaceObject))
+        return false;
+    if (m_moduleEnvironment && !Heap::isMarked(m_moduleEnvironment))
+        return false;
+    return true;
+}
+
 void GetByIdStatus::dump(PrintStream& out) const
 {
     out.print("(");
index a309bf9..e057fb3 100644 (file)
 #include "CallLinkStatus.h"
 #include "CodeOrigin.h"
 #include "ConcurrentJSLock.h"
+#include "ExitFlag.h"
 #include "GetByIdVariant.h"
+#include "ICStatusMap.h"
 #include "ScopeOffset.h"
+#include "StubInfoSummary.h"
 
 namespace JSC {
 
@@ -40,8 +43,6 @@ class JSModuleNamespaceObject;
 class ModuleNamespaceAccessCase;
 class StructureStubInfo;
 
-typedef HashMap<CodeOrigin, StructureStubInfo*, CodeOriginApproximateHash> StubInfoMap;
-
 class GetByIdStatus {
 public:
     enum State {
@@ -70,7 +71,27 @@ public:
     {
         ASSERT(state == NoInformation || state == TakesSlowPath || state == MakesCalls);
     }
-
+    
+    explicit GetByIdStatus(StubInfoSummary summary)
+        : m_wasSeenInJIT(true)
+    {
+        switch (summary) {
+        case StubInfoSummary::NoInformation:
+            m_state = NoInformation;
+            return;
+        case StubInfoSummary::Simple:
+        case StubInfoSummary::MakesCalls:
+            RELEASE_ASSERT_NOT_REACHED();
+            return;
+        case StubInfoSummary::TakesSlowPath:
+            m_state = TakesSlowPath;
+            return;
+        case StubInfoSummary::TakesSlowPathAndMakesCalls:
+            m_state = MakesCalls;
+            return;
+        }
+        RELEASE_ASSERT_NOT_REACHED();
+    }
     
     GetByIdStatus(
         State state, bool wasSeenInJIT, const GetByIdVariant& variant = GetByIdVariant())
@@ -81,10 +102,10 @@ public:
         m_variants.append(variant);
     }
     
-    static GetByIdStatus computeFor(CodeBlock*, StubInfoMap&, unsigned bytecodeIndex, UniquedStringImpl* uid);
+    static GetByIdStatus computeFor(CodeBlock*, ICStatusMap&, unsigned bytecodeIndex, UniquedStringImpl* uid, ExitFlag, CallLinkStatus::ExitSiteData);
     static GetByIdStatus computeFor(const StructureSet&, UniquedStringImpl* uid);
     
-    static GetByIdStatus computeFor(CodeBlock* baselineBlock, CodeBlock* dfgBlock, StubInfoMap& baselineMap, StubInfoMap& dfgMap, CodeOrigin, UniquedStringImpl* uid);
+    static GetByIdStatus computeFor(CodeBlock* baselineBlock, ICStatusMap& baselineMap, ICStatusContextStack& dfgContextStack, CodeOrigin, UniquedStringImpl* uid);
 
 #if ENABLE(DFG_JIT)
     static GetByIdStatus computeForStubInfo(const ConcurrentJSLocker&, CodeBlock* baselineBlock, StructureStubInfo*, CodeOrigin, UniquedStringImpl* uid);
@@ -106,8 +127,12 @@ public:
     bool takesSlowPath() const { return m_state == TakesSlowPath || m_state == MakesCalls || m_state == Custom || m_state == ModuleNamespace; }
     bool makesCalls() const;
     
+    GetByIdStatus slowVersion() const;
+    
     bool wasSeenInJIT() const { return m_wasSeenInJIT; }
     
+    void merge(const GetByIdStatus&);
+    
     // Attempts to reduce the set of variants to fit the given structure set. This may be approximate.
     void filter(const StructureSet&);
 
@@ -115,12 +140,12 @@ public:
     JSModuleEnvironment* moduleEnvironment() const { return m_moduleEnvironment; }
     ScopeOffset scopeOffset() const { return m_scopeOffset; }
     
+    void markIfCheap(SlotVisitor&);
+    bool finalize(); // Return true if this gets to live.
+    
     void dump(PrintStream&) const;
     
 private:
-#if ENABLE(DFG_JIT)
-    static bool hasExitSite(CodeBlock*, unsigned bytecodeIndex);
-#endif
 #if ENABLE(JIT)
     GetByIdStatus(const ModuleNamespaceAccessCase&);
     static GetByIdStatus computeForStubInfoWithoutExitSiteFeedback(
index bbe8efb..e437140 100644 (file)
@@ -103,8 +103,11 @@ bool GetByIdVariant::attemptToMerge(const GetByIdVariant& other)
 {
     if (m_offset != other.m_offset)
         return false;
-    if (m_callLinkStatus || other.m_callLinkStatus)
-        return false;
+    
+    if (m_callLinkStatus || other.m_callLinkStatus) {
+        if (!(m_callLinkStatus && other.m_callLinkStatus))
+            return false;
+    }
 
     if (!canMergeIntrinsicStructures(other))
         return false;
@@ -132,6 +135,27 @@ bool GetByIdVariant::attemptToMerge(const GetByIdVariant& other)
     
     m_structureSet.merge(other.m_structureSet);
     
+    if (m_callLinkStatus)
+        m_callLinkStatus->merge(*other.m_callLinkStatus);
+
+    return true;
+}
+
+void GetByIdVariant::markIfCheap(SlotVisitor& visitor)
+{
+    m_structureSet.markIfCheap(visitor);
+}
+
+bool GetByIdVariant::finalize()
+{
+    if (!m_structureSet.isStillAlive())
+        return false;
+    if (!m_conditionSet.areStillLive())
+        return false;
+    if (m_callLinkStatus && !m_callLinkStatus->finalize())
+        return false;
+    if (m_intrinsicFunction && !Heap::isMarked(m_intrinsicFunction))
+        return false;
     return true;
 }
 
index cf2814a..02f107d 100644 (file)
@@ -73,6 +73,9 @@ public:
 
     bool attemptToMerge(const GetByIdVariant& other);
     
+    void markIfCheap(SlotVisitor&);
+    bool finalize();
+    
     void dump(PrintStream&) const;
     void dumpInContext(PrintStream&, DumpContext*) const;
     
diff --git a/Source/JavaScriptCore/bytecode/ICStatusMap.cpp b/Source/JavaScriptCore/bytecode/ICStatusMap.cpp
new file mode 100644 (file)
index 0000000..7c9e699
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2018 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 "ICStatusMap.h"
+
+#include "InlineCallFrame.h"
+#include "TerminatedCodeOrigin.h"
+
+namespace JSC {
+
+ICStatus ICStatusContext::get(CodeOrigin codeOrigin) const
+{
+    return map.get<TerminatedCodeOriginHashTranslator>(
+        TerminatedCodeOrigin(codeOrigin, inlineCallFrame));
+}
+
+bool ICStatusContext::isInlined(CodeOrigin codeOrigin) const
+{
+    return codeOrigin.inlineCallFrame && codeOrigin.inlineCallFrame != inlineCallFrame;
+}
+
+ExitingInlineKind ICStatusContext::inlineKind(CodeOrigin codeOrigin) const
+{
+    return isInlined(codeOrigin) ? ExitFromInlined : ExitFromNotInlined;
+}
+
+} // namespace JSC
+
diff --git a/Source/JavaScriptCore/bytecode/ICStatusMap.h b/Source/JavaScriptCore/bytecode/ICStatusMap.h
new file mode 100644 (file)
index 0000000..40b3144
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2018 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. 
+ */
+
+#pragma once
+
+#include "CodeOrigin.h"
+#include "ExitingInlineKind.h"
+#include <wtf/HashMap.h>
+
+namespace JSC {
+
+class CallLinkInfo;
+class CallLinkStatus;
+class CodeBlock;
+class GetByIdStatus;
+class InByIdStatus;
+class PutByIdStatus;
+class StructureStubInfo;
+struct ByValInfo;
+
+struct ICStatus {
+    StructureStubInfo* stubInfo { nullptr };
+    CallLinkInfo* callLinkInfo { nullptr };
+    ByValInfo* byValInfo { nullptr };
+    CallLinkStatus* callStatus { nullptr };
+    GetByIdStatus* getStatus { nullptr };
+    InByIdStatus* inStatus { nullptr };
+    PutByIdStatus* putStatus { nullptr };
+};
+
+typedef HashMap<CodeOrigin, ICStatus, CodeOriginApproximateHash> ICStatusMap;
+
+struct ICStatusContext {
+    ICStatus get(CodeOrigin) const;
+    bool isInlined(CodeOrigin) const;
+    ExitingInlineKind inlineKind(CodeOrigin) const;
+    
+    InlineCallFrame* inlineCallFrame { nullptr };
+    CodeBlock* optimizedCodeBlock { nullptr };
+    ICStatusMap map;
+};
+
+typedef Vector<ICStatusContext*, 8> ICStatusContextStack;
+
+} // namespace JSC
+
diff --git a/Source/JavaScriptCore/bytecode/ICStatusUtils.cpp b/Source/JavaScriptCore/bytecode/ICStatusUtils.cpp
new file mode 100644 (file)
index 0000000..add2eb0
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2018 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 "ICStatusUtils.h"
+
+#include "CodeBlock.h"
+#include "ExitFlag.h"
+#include "UnlinkedCodeBlock.h"
+
+namespace JSC {
+
+ExitFlag hasBadCacheExitSite(CodeBlock* profiledBlock, unsigned bytecodeIndex)
+{
+#if ENABLE(DFG_JIT)
+    UnlinkedCodeBlock* unlinkedCodeBlock = profiledBlock->unlinkedCodeBlock();
+    ConcurrentJSLocker locker(unlinkedCodeBlock->m_lock);
+    auto exitFlag = [&] (ExitKind exitKind) -> ExitFlag {
+        auto withInlined = [&] (ExitingInlineKind inlineKind) -> ExitFlag {
+            return ExitFlag(unlinkedCodeBlock->hasExitSite(locker, DFG::FrequentExitSite(bytecodeIndex, exitKind, ExitFromAnything, inlineKind)), inlineKind);
+        };
+        return withInlined(ExitFromNotInlined) | withInlined(ExitFromInlined);
+    };
+    return exitFlag(BadCache) | exitFlag(BadConstantCache) | exitFlag(BadType);
+#else
+    UNUSED_PARAM(profiledBlock);
+    UNUSED_PARAM(bytecodeIndex);
+    return ExitFlag();
+#endif
+}
+
+} // namespace JSC
+
index 9003e5c..067a997 100644 (file)
@@ -25,6 +25,8 @@
 
 #pragma once
 
+#include "ExitFlag.h"
+
 namespace JSC {
 
 template<typename VariantVectorType, typename VariantType>
@@ -51,13 +53,14 @@ bool appendICStatusVariant(VariantVectorType& variants, const VariantType& varia
 template<typename VariantVectorType>
 void filterICStatusVariants(VariantVectorType& variants, const StructureSet& set)
 {
-    // FIXME: We could also filter the variants themselves.
-    
     variants.removeAllMatching(
         [&] (auto& variant) -> bool {
-            return !variant.structureSet().overlaps(set);
+            variant.structureSet().filter(set);
+            return variant.structureSet().isEmpty();
         });
 }
 
+ExitFlag hasBadCacheExitSite(CodeBlock* profiledBlock, unsigned bytecodeIndex);
+
 } // namespace JSC
 
index 779210d..112c804 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2018 Yusuke Suzuki <utatane.tea@gmail.com>.
+ * Copyright (C) 2018 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -41,16 +42,16 @@ bool InByIdStatus::appendVariant(const InByIdVariant& variant)
     return appendICStatusVariant(m_variants, variant);
 }
 
-InByIdStatus InByIdStatus::computeFor(CodeBlock* profiledBlock, StubInfoMap& map, unsigned bytecodeIndex, UniquedStringImpl* uid)
+InByIdStatus InByIdStatus::computeFor(CodeBlock* profiledBlock, ICStatusMap& map, unsigned bytecodeIndex, UniquedStringImpl* uid, ExitFlag didExit)
 {
     ConcurrentJSLocker locker(profiledBlock->m_lock);
 
     InByIdStatus result;
 
 #if ENABLE(DFG_JIT)
-    result = computeForStubInfoWithoutExitSiteFeedback(locker, map.get(CodeOrigin(bytecodeIndex)), uid);
+    result = computeForStubInfoWithoutExitSiteFeedback(locker, map.get(CodeOrigin(bytecodeIndex)).stubInfo, uid);
 
-    if (!result.takesSlowPath() && hasExitSite(profiledBlock, bytecodeIndex))
+    if (!result.takesSlowPath() && didExit)
         return InByIdStatus(TakesSlowPath);
 #else
     UNUSED_PARAM(map);
@@ -61,68 +62,65 @@ InByIdStatus InByIdStatus::computeFor(CodeBlock* profiledBlock, StubInfoMap& map
     return result;
 }
 
-InByIdStatus InByIdStatus::computeFor(
-    CodeBlock* profiledBlock, CodeBlock* dfgBlock, StubInfoMap& baselineMap,
-    StubInfoMap& dfgMap, CodeOrigin codeOrigin, UniquedStringImpl* uid)
+InByIdStatus InByIdStatus::computeFor(CodeBlock* profiledBlock, ICStatusMap& map, unsigned bytecodeIndex, UniquedStringImpl* uid)
 {
-#if ENABLE(DFG_JIT)
-    if (dfgBlock) {
-        CallLinkStatus::ExitSiteData exitSiteData;
-        {
-            ConcurrentJSLocker locker(profiledBlock->m_lock);
-            exitSiteData = CallLinkStatus::computeExitSiteData(
-                profiledBlock, codeOrigin.bytecodeIndex);
-        }
-
-        InByIdStatus result;
-        {
-            ConcurrentJSLocker locker(dfgBlock->m_lock);
-            result = computeForStubInfoWithoutExitSiteFeedback(locker, dfgMap.get(codeOrigin), uid);
-        }
-
-        if (result.takesSlowPath())
-            return result;
-
-        if (hasExitSite(profiledBlock, codeOrigin.bytecodeIndex))
-            return InByIdStatus(TakesSlowPath);
+    return computeFor(profiledBlock, map, bytecodeIndex, uid, hasBadCacheExitSite(profiledBlock, bytecodeIndex));
+}
 
-        if (result.isSet())
+InByIdStatus InByIdStatus::computeFor(
+    CodeBlock* profiledBlock, ICStatusMap& baselineMap,
+    ICStatusContextStack& contextStack, CodeOrigin codeOrigin, UniquedStringImpl* uid)
+{
+    ExitFlag didExit = hasBadCacheExitSite(profiledBlock, codeOrigin.bytecodeIndex);
+    
+    for (ICStatusContext* context : contextStack) {
+        ICStatus status = context->get(codeOrigin);
+        
+        auto bless = [&] (const InByIdStatus& result) -> InByIdStatus {
+            if (!context->isInlined(codeOrigin)) {
+                InByIdStatus baselineResult = computeFor(
+                    profiledBlock, baselineMap, codeOrigin.bytecodeIndex, uid, didExit);
+                baselineResult.merge(result);
+                return baselineResult;
+            }
+            if (didExit.isSet(ExitFromInlined))
+                return InByIdStatus(TakesSlowPath);
             return result;
+        };
+        
+        if (status.stubInfo) {
+            InByIdStatus result;
+            {
+                ConcurrentJSLocker locker(context->optimizedCodeBlock->m_lock);
+                result = computeForStubInfoWithoutExitSiteFeedback(locker, status.stubInfo, uid);
+            }
+            if (result.isSet())
+                return bless(result);
+        }
+        
+        if (status.inStatus)
+            return bless(*status.inStatus);
     }
-#else
-    UNUSED_PARAM(dfgBlock);
-    UNUSED_PARAM(dfgMap);
-#endif
-
-    return computeFor(profiledBlock, baselineMap, codeOrigin.bytecodeIndex, uid);
+    
+    return computeFor(profiledBlock, baselineMap, codeOrigin.bytecodeIndex, uid, didExit);
 }
 
 #if ENABLE(DFG_JIT)
-bool InByIdStatus::hasExitSite(CodeBlock* profiledBlock, unsigned bytecodeIndex)
-{
-    UnlinkedCodeBlock* unlinkedCodeBlock = profiledBlock->unlinkedCodeBlock();
-    ConcurrentJSLocker locker(unlinkedCodeBlock->m_lock);
-    return unlinkedCodeBlock->hasExitSite(locker, DFG::FrequentExitSite(bytecodeIndex, BadCache))
-        || unlinkedCodeBlock->hasExitSite(locker, DFG::FrequentExitSite(bytecodeIndex, BadConstantCache));
-}
-
 InByIdStatus InByIdStatus::computeForStubInfo(const ConcurrentJSLocker& locker, CodeBlock* profiledBlock, StructureStubInfo* stubInfo, CodeOrigin codeOrigin, UniquedStringImpl* uid)
 {
     InByIdStatus result = InByIdStatus::computeForStubInfoWithoutExitSiteFeedback(locker, stubInfo, uid);
 
-    if (!result.takesSlowPath() && InByIdStatus::hasExitSite(profiledBlock, codeOrigin.bytecodeIndex))
+    if (!result.takesSlowPath() && hasBadCacheExitSite(profiledBlock, codeOrigin.bytecodeIndex))
         return InByIdStatus(TakesSlowPath);
     return result;
 }
 
 InByIdStatus InByIdStatus::computeForStubInfoWithoutExitSiteFeedback(const ConcurrentJSLocker&, StructureStubInfo* stubInfo, UniquedStringImpl* uid)
 {
-    if (!stubInfo || !stubInfo->everConsidered)
-        return InByIdStatus(NoInformation);
-
-    if (stubInfo->tookSlowPath)
-        return InByIdStatus(TakesSlowPath);
-
+    StubInfoSummary summary = StructureStubInfo::summary(stubInfo);
+    if (!isInlineable(summary))
+        return InByIdStatus(summary);
+    
     // Finally figure out if we can derive an access strategy.
     InByIdStatus result;
     result.m_state = Simple;
@@ -209,6 +207,36 @@ InByIdStatus InByIdStatus::computeForStubInfoWithoutExitSiteFeedback(const Concu
 }
 #endif
 
+void InByIdStatus::merge(const InByIdStatus& other)
+{
+    if (other.m_state == NoInformation)
+        return;
+    
+    switch (m_state) {
+    case NoInformation:
+        *this = other;
+        return;
+        
+    case Simple:
+        if (other.m_state != Simple) {
+            *this = InByIdStatus(TakesSlowPath);
+            return;
+        }
+        for (const InByIdVariant& otherVariant : other.m_variants) {
+            if (!appendVariant(otherVariant)) {
+                *this = InByIdStatus(TakesSlowPath);
+                return;
+            }
+        }
+        return;
+        
+    case TakesSlowPath:
+        return;
+    }
+    
+    RELEASE_ASSERT_NOT_REACHED();
+}
+
 void InByIdStatus::filter(const StructureSet& structureSet)
 {
     if (m_state != Simple)
@@ -218,6 +246,21 @@ void InByIdStatus::filter(const StructureSet& structureSet)
         m_state = NoInformation;
 }
 
+void InByIdStatus::markIfCheap(SlotVisitor& visitor)
+{
+    for (InByIdVariant& variant : m_variants)
+        variant.markIfCheap(visitor);
+}
+
+bool InByIdStatus::finalize()
+{
+    for (InByIdVariant& variant : m_variants) {
+        if (!variant.finalize())
+            return false;
+    }
+    return true;
+}
+
 void InByIdStatus::dump(PrintStream& out) const
 {
     out.print("(");
index 6615dfc..57ab9ed 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2018 Yusuke Suzuki <utatane.tea@gmail.com>.
+ * Copyright (C) 2018 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -28,7 +29,9 @@
 #include "CallLinkStatus.h"
 #include "CodeOrigin.h"
 #include "ConcurrentJSLock.h"
+#include "ICStatusMap.h"
 #include "InByIdVariant.h"
+#include "StubInfoSummary.h"
 
 namespace JSC {
 
@@ -36,8 +39,6 @@ class AccessCase;
 class CodeBlock;
 class StructureStubInfo;
 
-typedef HashMap<CodeOrigin, StructureStubInfo*, CodeOriginApproximateHash> StubInfoMap;
-
 class InByIdStatus {
 public:
     enum State {
@@ -56,11 +57,31 @@ public:
         : m_state(state)
     {
         ASSERT((state == Simple) == variant.isSet());
-        m_variants.append(variant);
+        if (variant.isSet())
+            m_variants.append(variant);
     }
 
-    static InByIdStatus computeFor(CodeBlock*, StubInfoMap&, unsigned bytecodeIndex, UniquedStringImpl* uid);
-    static InByIdStatus computeFor(CodeBlock* baselineBlock, CodeBlock* dfgBlock, StubInfoMap& baselineMap, StubInfoMap& dfgMap, CodeOrigin, UniquedStringImpl* uid);
+    explicit InByIdStatus(StubInfoSummary summary)
+    {
+        switch (summary) {
+        case StubInfoSummary::NoInformation:
+            m_state = NoInformation;
+            return;
+        case StubInfoSummary::Simple:
+        case StubInfoSummary::MakesCalls:
+            RELEASE_ASSERT_NOT_REACHED();
+            return;
+        case StubInfoSummary::TakesSlowPath:
+        case StubInfoSummary::TakesSlowPathAndMakesCalls:
+            m_state = TakesSlowPath;
+            return;
+        }
+        RELEASE_ASSERT_NOT_REACHED();
+    }
+    
+    static InByIdStatus computeFor(CodeBlock*, ICStatusMap&, unsigned bytecodeIndex, UniquedStringImpl* uid);
+    static InByIdStatus computeFor(CodeBlock*, ICStatusMap&, unsigned bytecodeIndex, UniquedStringImpl* uid, ExitFlag);
+    static InByIdStatus computeFor(CodeBlock* baselineBlock, ICStatusMap& baselineMap, ICStatusContextStack& contextStack, CodeOrigin, UniquedStringImpl* uid);
 
 #if ENABLE(DFG_JIT)
     static InByIdStatus computeForStubInfo(const ConcurrentJSLocker&, CodeBlock* baselineBlock, StructureStubInfo*, CodeOrigin, UniquedStringImpl* uid);
@@ -78,15 +99,19 @@ public:
     const InByIdVariant& operator[](size_t index) const { return at(index); }
 
     bool takesSlowPath() const { return m_state == TakesSlowPath; }
+    
+    void merge(const InByIdStatus&);
 
     // Attempts to reduce the set of variants to fit the given structure set. This may be approximate.
     void filter(const StructureSet&);
+    
+    void markIfCheap(SlotVisitor&);
+    bool finalize();
 
     void dump(PrintStream&) const;
 
 private:
 #if ENABLE(DFG_JIT)
-    static bool hasExitSite(CodeBlock*, unsigned bytecodeIndex);
     static InByIdStatus computeForStubInfoWithoutExitSiteFeedback(const ConcurrentJSLocker&, StructureStubInfo*, UniquedStringImpl* uid);
 #endif
     bool appendVariant(const InByIdVariant&);
index 626a5d3..a7c3cd6 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2018 Yusuke Suzuki <utatane.tea@gmail.com>.
+ * Copyright (C) 2018 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -63,6 +64,20 @@ bool InByIdVariant::attemptToMerge(const InByIdVariant& other)
     return true;
 }
 
+void InByIdVariant::markIfCheap(SlotVisitor& visitor)
+{
+    m_structureSet.markIfCheap(visitor);
+}
+
+bool InByIdVariant::finalize()
+{
+    if (!m_structureSet.isStillAlive())
+        return false;
+    if (!m_conditionSet.areStillLive())
+        return false;
+    return true;
+}
+
 void InByIdVariant::dump(PrintStream& out) const
 {
     dumpInContext(out, 0);
index b7898a3..dd20911 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2018 Yusuke Suzuki <utatane.tea@gmail.com>.
+ * Copyright (C) 2018 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -54,6 +55,9 @@ public:
     bool isHit() const { return offset() != invalidOffset; }
 
     bool attemptToMerge(const InByIdVariant& other);
+    
+    void markIfCheap(SlotVisitor&);
+    bool finalize();
 
     void dump(PrintStream&) const;
     void dumpInContext(PrintStream&, DumpContext*) const;
index ca07c35..d3ba9c9 100644 (file)
@@ -27,6 +27,7 @@
 #include "InstanceOfStatus.h"
 
 #include "ICStatusUtils.h"
+#include "InstanceOfAccessCase.h"
 #include "JSCInlines.h"
 #include "PolymorphicAccess.h"
 #include "StructureStubInfo.h"
@@ -39,13 +40,13 @@ void InstanceOfStatus::appendVariant(const InstanceOfVariant& variant)
 }
 
 InstanceOfStatus InstanceOfStatus::computeFor(
-    CodeBlock* codeBlock, StubInfoMap& infoMap, unsigned bytecodeIndex)
+    CodeBlock* codeBlock, ICStatusMap& infoMap, unsigned bytecodeIndex)
 {
     ConcurrentJSLocker locker(codeBlock->m_lock);
     
     InstanceOfStatus result;
 #if ENABLE(DFG_JIT)
-    result = computeForStubInfo(locker, infoMap.get(CodeOrigin(bytecodeIndex)));
+    result = computeForStubInfo(locker, infoMap.get(CodeOrigin(bytecodeIndex)).stubInfo);
 
     if (!result.takesSlowPath()) {
         UnlinkedCodeBlock* unlinkedCodeBlock = codeBlock->unlinkedCodeBlock();
@@ -67,14 +68,12 @@ InstanceOfStatus InstanceOfStatus::computeFor(
 #if ENABLE(DFG_JIT)
 InstanceOfStatus InstanceOfStatus::computeForStubInfo(const ConcurrentJSLocker&, StructureStubInfo* stubInfo)
 {
-    if (!stubInfo || !stubInfo->everConsidered)
-        return NoInformation;
-    
     // FIXME: We wouldn't have to bail for nonCell if we taught MatchStructure how to handle non
-    // cells.
+    // cells. If we fixed that then we wouldn't be able to use summary();
     // https://bugs.webkit.org/show_bug.cgi?id=185784
-    if (stubInfo->tookSlowPath || stubInfo->sawNonCell)
-        return TakesSlowPath;
+    StubInfoSummary summary = StructureStubInfo::summary(stubInfo);
+    if (!isInlineable(summary))
+        return InstanceOfStatus(summary);
     
     if (stubInfo->cacheType != CacheType::Stub)
         return TakesSlowPath; // This is conservative. It could be that we have no information.
index fe466c4..13ca98e 100644 (file)
@@ -27,7 +27,9 @@
 
 #include "CodeOrigin.h"
 #include "ConcurrentJSLock.h"
+#include "ICStatusMap.h"
 #include "InstanceOfVariant.h"
+#include "StubInfoSummary.h"
 
 namespace JSC {
 
@@ -35,8 +37,6 @@ class AccessCase;
 class CodeBlock;
 class StructureStubInfo;
 
-typedef HashMap<CodeOrigin, StructureStubInfo*, CodeOriginApproximateHash> StubInfoMap;
-
 class InstanceOfStatus {
 public:
     enum State {
@@ -61,7 +61,25 @@ public:
         ASSERT(state == NoInformation || state == TakesSlowPath);
     }
     
-    static InstanceOfStatus computeFor(CodeBlock*, StubInfoMap&, unsigned bytecodeIndex);
+    explicit InstanceOfStatus(StubInfoSummary summary)
+    {
+        switch (summary) {
+        case StubInfoSummary::NoInformation:
+            m_state = NoInformation;
+            return;
+        case StubInfoSummary::Simple:
+        case StubInfoSummary::MakesCalls:
+            RELEASE_ASSERT_NOT_REACHED();
+            return;
+        case StubInfoSummary::TakesSlowPath:
+        case StubInfoSummary::TakesSlowPathAndMakesCalls:
+            m_state = TakesSlowPath;
+            return;
+        }
+        RELEASE_ASSERT_NOT_REACHED();
+    }
+    
+    static InstanceOfStatus computeFor(CodeBlock*, ICStatusMap&, unsigned bytecodeIndex);
     
 #if ENABLE(DFG_JIT)
     static InstanceOfStatus computeForStubInfo(const ConcurrentJSLocker&, StructureStubInfo*);
index f74c23d..aac974c 100644 (file)
@@ -29,6 +29,7 @@
 #include "CodeBlock.h"
 #include "ComplexGetStatus.h"
 #include "GetterSetterAccessCase.h"
+#include "ICStatusUtils.h"
 #include "LLIntData.h"
 #include "LowLevelInterpreter.h"
 #include "JSCInlines.h"
@@ -42,25 +43,13 @@ namespace JSC {
 
 bool PutByIdStatus::appendVariant(const PutByIdVariant& variant)
 {
-    for (unsigned i = 0; i < m_variants.size(); ++i) {
-        if (m_variants[i].attemptToMerge(variant))
-            return true;
-    }
-    for (unsigned i = 0; i < m_variants.size(); ++i) {
-        if (m_variants[i].oldStructure().overlaps(variant.oldStructure()))
-            return false;
-    }
-    m_variants.append(variant);
-    return true;
+    return appendICStatusVariant(m_variants, variant);
 }
 
 #if ENABLE(DFG_JIT)
-bool PutByIdStatus::hasExitSite(CodeBlock* profiledBlock, unsigned bytecodeIndex)
+ExitFlag PutByIdStatus::hasExitSite(CodeBlock* profiledBlock, unsigned bytecodeIndex)
 {
-    UnlinkedCodeBlock* unlinkedCodeBlock = profiledBlock->unlinkedCodeBlock();
-    ConcurrentJSLocker locker(unlinkedCodeBlock->m_lock);
-    return unlinkedCodeBlock->hasExitSite(locker, DFG::FrequentExitSite(bytecodeIndex, BadCache))
-        || unlinkedCodeBlock->hasExitSite(locker, DFG::FrequentExitSite(bytecodeIndex, BadConstantCache));
+    return hasBadCacheExitSite(profiledBlock, bytecodeIndex);
 }
 #endif
 
@@ -110,7 +99,7 @@ PutByIdStatus PutByIdStatus::computeFromLLInt(CodeBlock* profiledBlock, unsigned
         structure, newStructure, conditionSet, offset, newStructure->inferredTypeDescriptorFor(uid));
 }
 
-PutByIdStatus PutByIdStatus::computeFor(CodeBlock* profiledBlock, StubInfoMap& map, unsigned bytecodeIndex, UniquedStringImpl* uid)
+PutByIdStatus PutByIdStatus::computeFor(CodeBlock* profiledBlock, ICStatusMap& map, unsigned bytecodeIndex, UniquedStringImpl* uid, ExitFlag didExit, CallLinkStatus::ExitSiteData callExitSiteData)
 {
     ConcurrentJSLocker locker(profiledBlock->m_lock);
     
@@ -118,13 +107,12 @@ PutByIdStatus PutByIdStatus::computeFor(CodeBlock* profiledBlock, StubInfoMap& m
     UNUSED_PARAM(bytecodeIndex);
     UNUSED_PARAM(uid);
 #if ENABLE(DFG_JIT)
-    if (hasExitSite(profiledBlock, bytecodeIndex))
+    if (didExit)
         return PutByIdStatus(TakesSlowPath);
     
-    StructureStubInfo* stubInfo = map.get(CodeOrigin(bytecodeIndex));
+    StructureStubInfo* stubInfo = map.get(CodeOrigin(bytecodeIndex)).stubInfo;
     PutByIdStatus result = computeForStubInfo(
-        locker, profiledBlock, stubInfo, uid,
-        CallLinkStatus::computeExitSiteData(profiledBlock, bytecodeIndex));
+        locker, profiledBlock, stubInfo, uid, callExitSiteData);
     if (!result)
         return computeFromLLInt(profiledBlock, bytecodeIndex, uid);
     
@@ -147,16 +135,14 @@ PutByIdStatus PutByIdStatus::computeForStubInfo(
     const ConcurrentJSLocker& locker, CodeBlock* profiledBlock, StructureStubInfo* stubInfo,
     UniquedStringImpl* uid, CallLinkStatus::ExitSiteData callExitSiteData)
 {
-    if (!stubInfo || !stubInfo->everConsidered)
-        return PutByIdStatus();
-    
-    if (stubInfo->tookSlowPath)
-        return PutByIdStatus(TakesSlowPath);
+    StubInfoSummary summary = StructureStubInfo::summary(stubInfo);
+    if (!isInlineable(summary))
+        return PutByIdStatus(summary);
     
     switch (stubInfo->cacheType) {
     case CacheType::Unset:
         // This means that we attempted to cache but failed for some reason.
-        return PutByIdStatus(TakesSlowPath);
+        return PutByIdStatus(JSC::slowVersion(summary));
         
     case CacheType::PutByIdReplace: {
         PropertyOffset offset =
@@ -165,7 +151,7 @@ PutByIdStatus PutByIdStatus::computeForStubInfo(
             return PutByIdVariant::replace(
                 stubInfo->u.byIdSelf.baseObjectStructure.get(), offset, InferredType::Top);
         }
-        return PutByIdStatus(TakesSlowPath);
+        return PutByIdStatus(JSC::slowVersion(summary));
     }
         
     case CacheType::Stub: {
@@ -174,19 +160,12 @@ PutByIdStatus PutByIdStatus::computeForStubInfo(
         PutByIdStatus result;
         result.m_state = Simple;
         
-        State slowPathState = TakesSlowPath;
-        for (unsigned i = 0; i < list->size(); ++i) {
-            const AccessCase& access = list->at(i);
-            if (access.doesCalls())
-                slowPathState = MakesCalls;
-        }
-        
         for (unsigned i = 0; i < list->size(); ++i) {
             const AccessCase& access = list->at(i);
             if (access.viaProxy())
-                return PutByIdStatus(slowPathState);
+                return PutByIdStatus(JSC::slowVersion(summary));
             if (access.usesPolyProto())
-                return PutByIdStatus(slowPathState);
+                return PutByIdStatus(JSC::slowVersion(summary));
             
             PutByIdVariant variant;
             
@@ -195,7 +174,7 @@ PutByIdStatus PutByIdStatus::computeForStubInfo(
                 Structure* structure = access.structure();
                 PropertyOffset offset = structure->getConcurrently(uid);
                 if (!isValidOffset(offset))
-                    return PutByIdStatus(slowPathState);
+                    return PutByIdStatus(JSC::slowVersion(summary));
                 variant = PutByIdVariant::replace(
                     structure, offset, structure->inferredTypeDescriptorFor(uid));
                 break;
@@ -205,10 +184,10 @@ PutByIdStatus PutByIdStatus::computeForStubInfo(
                 PropertyOffset offset =
                     access.newStructure()->getConcurrently(uid);
                 if (!isValidOffset(offset))
-                    return PutByIdStatus(slowPathState);
+                    return PutByIdStatus(JSC::slowVersion(summary));
                 ObjectPropertyConditionSet conditionSet = access.conditionSet();
                 if (!conditionSet.structuresEnsureValidity())
-                    return PutByIdStatus(slowPathState);
+                    return PutByIdStatus(JSC::slowVersion(summary));
                 variant = PutByIdVariant::transition(
                     access.structure(), access.newStructure(), conditionSet, offset,
                     access.newStructure()->inferredTypeDescriptorFor(uid));
@@ -226,7 +205,7 @@ PutByIdStatus PutByIdStatus::computeForStubInfo(
                     continue;
                     
                 case ComplexGetStatus::TakesSlowPath:
-                    return PutByIdStatus(slowPathState);
+                    return PutByIdStatus(JSC::slowVersion(summary));
                     
                 case ComplexGetStatus::Inlineable: {
                     std::unique_ptr<CallLinkStatus> callLinkStatus =
@@ -248,54 +227,60 @@ PutByIdStatus PutByIdStatus::computeForStubInfo(
                 return PutByIdStatus(MakesCalls);
 
             default:
-                return PutByIdStatus(slowPathState);
+                return PutByIdStatus(JSC::slowVersion(summary));
             }
             
             if (!result.appendVariant(variant))
-                return PutByIdStatus(slowPathState);
+                return PutByIdStatus(JSC::slowVersion(summary));
         }
         
         return result;
     }
         
     default:
-        return PutByIdStatus(TakesSlowPath);
+        return PutByIdStatus(JSC::slowVersion(summary));
     }
 }
 #endif
 
-PutByIdStatus PutByIdStatus::computeFor(CodeBlock* baselineBlock, CodeBlock* dfgBlock, StubInfoMap& baselineMap, StubInfoMap& dfgMap, CodeOrigin codeOrigin, UniquedStringImpl* uid)
+PutByIdStatus PutByIdStatus::computeFor(CodeBlock* baselineBlock, ICStatusMap& baselineMap, ICStatusContextStack& contextStack, CodeOrigin codeOrigin, UniquedStringImpl* uid)
 {
-#if ENABLE(DFG_JIT)
-    if (dfgBlock) {
-        if (hasExitSite(baselineBlock, codeOrigin.bytecodeIndex))
-            return PutByIdStatus(TakesSlowPath);
-        CallLinkStatus::ExitSiteData exitSiteData;
-        {
-            ConcurrentJSLocker locker(baselineBlock->m_lock);
-            exitSiteData = CallLinkStatus::computeExitSiteData(
-                baselineBlock, codeOrigin.bytecodeIndex);
-        }
-            
-        PutByIdStatus result;
-        {
-            ConcurrentJSLocker locker(dfgBlock->m_lock);
-            result = computeForStubInfo(
-                locker, dfgBlock, dfgMap.get(codeOrigin), uid, exitSiteData);
-        }
+    CallLinkStatus::ExitSiteData callExitSiteData =
+        CallLinkStatus::computeExitSiteData(baselineBlock, codeOrigin.bytecodeIndex);
+    ExitFlag didExit = hasExitSite(baselineBlock, codeOrigin.bytecodeIndex);
+
+    for (ICStatusContext* context : contextStack) {
+        ICStatus status = context->get(codeOrigin);
         
-        // We use TakesSlowPath in some cases where the stub was unset. That's weird and
-        // it would be better not to do that. But it means that we have to defend
-        // ourselves here.
-        if (result.isSimple())
+        auto bless = [&] (const PutByIdStatus& result) -> PutByIdStatus {
+            if (!context->isInlined(codeOrigin)) {
+                PutByIdStatus baselineResult = computeFor(
+                    baselineBlock, baselineMap, codeOrigin.bytecodeIndex, uid, didExit,
+                    callExitSiteData);
+                baselineResult.merge(result);
+                return baselineResult;
+            }
+            if (didExit.isSet(ExitFromInlined))
+                return result.slowVersion();
             return result;
+        };
+        
+        if (status.stubInfo) {
+            PutByIdStatus result;
+            {
+                ConcurrentJSLocker locker(context->optimizedCodeBlock->m_lock);
+                result = computeForStubInfo(
+                    locker, context->optimizedCodeBlock, status.stubInfo, uid, callExitSiteData);
+            }
+            if (result.isSet())
+                return bless(result);
+        }
+        
+        if (status.putStatus)
+            return bless(*status.putStatus);
     }
-#else
-    UNUSED_PARAM(dfgBlock);
-    UNUSED_PARAM(dfgMap);
-#endif
-
-    return computeFor(baselineBlock, baselineMap, codeOrigin.bytecodeIndex, uid);
+    
+    return computeFor(baselineBlock, baselineMap, codeOrigin.bytecodeIndex, uid, didExit, callExitSiteData);
 }
 
 PutByIdStatus PutByIdStatus::computeFor(JSGlobalObject* globalObject, const StructureSet& set, UniquedStringImpl* uid, bool isDirect)
@@ -398,6 +383,69 @@ bool PutByIdStatus::makesCalls() const
     return false;
 }
 
+PutByIdStatus PutByIdStatus::slowVersion() const
+{
+    return PutByIdStatus(makesCalls() ? MakesCalls : TakesSlowPath);
+}
+
+void PutByIdStatus::markIfCheap(SlotVisitor& visitor)
+{
+    for (PutByIdVariant& variant : m_variants)
+        variant.markIfCheap(visitor);
+}
+
+bool PutByIdStatus::finalize()
+{
+    for (PutByIdVariant& variant : m_variants) {
+        if (!variant.finalize())
+            return false;
+    }
+    return true;
+}
+
+void PutByIdStatus::merge(const PutByIdStatus& other)
+{
+    if (other.m_state == NoInformation)
+        return;
+    
+    auto mergeSlow = [&] () {
+        *this = PutByIdStatus((makesCalls() || other.makesCalls()) ? MakesCalls : TakesSlowPath);
+    };
+    
+    switch (m_state) {
+    case NoInformation:
+        *this = other;
+        return;
+        
+    case Simple:
+        if (other.m_state != Simple)
+            return mergeSlow();
+        
+        for (const PutByIdVariant& other : other.m_variants) {
+            if (!appendVariant(other))
+                return mergeSlow();
+        }
+        return;
+        
+    case TakesSlowPath:
+    case MakesCalls:
+        return mergeSlow();
+    }
+    
+    RELEASE_ASSERT_NOT_REACHED();
+}
+
+void PutByIdStatus::filter(const StructureSet& set)
+{
+    if (m_state != Simple)
+        return;
+    filterICStatusVariants(m_variants, set);
+    for (PutByIdVariant& variant : m_variants)
+        variant.fixTransitionToReplaceIfNecessary();
+    if (m_variants.isEmpty())
+        m_state = NoInformation;
+}
+
 void PutByIdStatus::dump(PrintStream& out) const
 {
     switch (m_state) {
index b5cd56a..02869ff 100644 (file)
 #pragma once
 
 #include "CallLinkStatus.h"
+#include "ExitFlag.h"
+#include "ICStatusMap.h"
 #include "PutByIdVariant.h"
+#include "StubInfoSummary.h"
 
 namespace JSC {
 
@@ -63,16 +66,36 @@ public:
         ASSERT(m_state == NoInformation || m_state == TakesSlowPath || m_state == MakesCalls);
     }
     
+    explicit PutByIdStatus(StubInfoSummary summary)
+    {
+        switch (summary) {
+        case StubInfoSummary::NoInformation:
+            m_state = NoInformation;
+            return;
+        case StubInfoSummary::Simple:
+        case StubInfoSummary::MakesCalls:
+            RELEASE_ASSERT_NOT_REACHED();
+            return;
+        case StubInfoSummary::TakesSlowPath:
+            m_state = TakesSlowPath;
+            return;
+        case StubInfoSummary::TakesSlowPathAndMakesCalls:
+            m_state = MakesCalls;
+            return;
+        }
+        RELEASE_ASSERT_NOT_REACHED();
+    }
+    
     PutByIdStatus(const PutByIdVariant& variant)
         : m_state(Simple)
     {
         m_variants.append(variant);
     }
     
-    static PutByIdStatus computeFor(CodeBlock*, StubInfoMap&, unsigned bytecodeIndex, UniquedStringImpl* uid);
+    static PutByIdStatus computeFor(CodeBlock*, ICStatusMap&, unsigned bytecodeIndex, UniquedStringImpl* uid, ExitFlag, CallLinkStatus::ExitSiteData);
     static PutByIdStatus computeFor(JSGlobalObject*, const StructureSet&, UniquedStringImpl* uid, bool isDirect);
     
-    static PutByIdStatus computeFor(CodeBlock* baselineBlock, CodeBlock* dfgBlock, StubInfoMap& baselineMap, StubInfoMap& dfgMap, CodeOrigin, UniquedStringImpl* uid);
+    static PutByIdStatus computeFor(CodeBlock* baselineBlock, ICStatusMap& baselineMap, ICStatusContextStack& contextStack, CodeOrigin, UniquedStringImpl* uid);
 
 #if ENABLE(JIT)
     static PutByIdStatus computeForStubInfo(const ConcurrentJSLocker&, CodeBlock* baselineBlock, StructureStubInfo*, CodeOrigin, UniquedStringImpl* uid);
@@ -85,17 +108,25 @@ public:
     bool isSimple() const { return m_state == Simple; }
     bool takesSlowPath() const { return m_state == TakesSlowPath || m_state == MakesCalls; }
     bool makesCalls() const;
+    PutByIdStatus slowVersion() const;
     
     size_t numVariants() const { return m_variants.size(); }
     const Vector<PutByIdVariant, 1>& variants() const { return m_variants; }
     const PutByIdVariant& at(size_t index) const { return m_variants[index]; }
     const PutByIdVariant& operator[](size_t index) const { return at(index); }
     
+    void markIfCheap(SlotVisitor&);
+    bool finalize();
+    
+    void merge(const PutByIdStatus&);
+    
+    void filter(const StructureSet&);
+    
     void dump(PrintStream&) const;
     
 private:
 #if ENABLE(DFG_JIT)
-    static bool hasExitSite(CodeBlock*, unsigned bytecodeIndex);
+    static ExitFlag hasExitSite(CodeBlock*, unsigned bytecodeIndex);
 #endif
 #if ENABLE(JIT)
     static PutByIdStatus computeForStubInfo(
index 9904c62..c39869d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014, 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2014-2018 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -96,8 +96,8 @@ PutByIdVariant PutByIdVariant::setter(
 
 Structure* PutByIdVariant::oldStructureForTransition() const
 {
-    ASSERT(kind() == Transition);
-    ASSERT(m_oldStructure.size() <= 2);
+    RELEASE_ASSERT(kind() == Transition);
+    RELEASE_ASSERT(m_oldStructure.size() <= 2);
     for (unsigned i = m_oldStructure.size(); i--;) {
         Structure* structure = m_oldStructure[i];
         if (structure != m_newStructure)
@@ -108,6 +108,24 @@ Structure* PutByIdVariant::oldStructureForTransition() const
     return nullptr;
 }
 
+void PutByIdVariant::fixTransitionToReplaceIfNecessary()
+{
+    if (kind() != Transition)
+        return;
+    
+    RELEASE_ASSERT(m_oldStructure.size() <= 2);
+    for (unsigned i = m_oldStructure.size(); i--;) {
+        Structure* structure = m_oldStructure[i];
+        if (structure != m_newStructure)
+            return;
+    }
+    
+    m_newStructure = nullptr;
+    m_kind = Replace;
+    m_conditionSet = ObjectPropertyConditionSet();
+    RELEASE_ASSERT(!m_callLinkStatus);
+}
+
 bool PutByIdVariant::writesStructures() const
 {
     switch (kind()) {
@@ -145,6 +163,10 @@ bool PutByIdVariant::attemptToMerge(const PutByIdVariant& other)
         return false;
     
     switch (m_kind) {
+    case NotSet:
+        RELEASE_ASSERT_NOT_REACHED();
+        return false;
+        
     case Replace: {
         switch (other.m_kind) {
         case Replace: {
@@ -174,13 +196,56 @@ bool PutByIdVariant::attemptToMerge(const PutByIdVariant& other)
         case Replace:
             return attemptToMergeTransitionWithReplace(other);
             
+        case Transition: {
+            if (m_oldStructure != other.m_oldStructure)
+                return false;
+            
+            if (m_newStructure != other.m_newStructure)
+                return false;
+            
+            ObjectPropertyConditionSet mergedConditionSet;
+            if (!m_conditionSet.isEmpty()) {
+                mergedConditionSet = m_conditionSet.mergedWith(other.m_conditionSet);
+                if (!mergedConditionSet.isValid())
+                    return false;
+            }
+            m_conditionSet = mergedConditionSet;
+            return true;
+        }
+            
         default:
             return false;
         }
         
-    default:
-        return false;
-    }
+    case Setter: {
+        if (other.m_kind != Setter)
+            return false;
+        
+        if (m_callLinkStatus || other.m_callLinkStatus) {
+            if (!(m_callLinkStatus && other.m_callLinkStatus))
+                return false;
+        }
+        
+        if (m_conditionSet.isEmpty() != other.m_conditionSet.isEmpty())
+            return false;
+        
+        ObjectPropertyConditionSet mergedConditionSet;
+        if (!m_conditionSet.isEmpty()) {
+            mergedConditionSet = m_conditionSet.mergedWith(other.m_conditionSet);
+            if (!mergedConditionSet.isValid() || !mergedConditionSet.hasOneSlotBaseCondition())
+                return false;
+        }
+        m_conditionSet = mergedConditionSet;
+        
+        if (m_callLinkStatus)
+            m_callLinkStatus->merge(*other.m_callLinkStatus);
+        
+        m_oldStructure.merge(other.m_oldStructure);
+        return true;
+    } }
+    
+    RELEASE_ASSERT_NOT_REACHED();
+    return false;
 }
 
 bool PutByIdVariant::attemptToMergeTransitionWithReplace(const PutByIdVariant& replace)
@@ -206,6 +271,26 @@ bool PutByIdVariant::attemptToMergeTransitionWithReplace(const PutByIdVariant& r
     return true;
 }
 
+void PutByIdVariant::markIfCheap(SlotVisitor& visitor)
+{
+    m_oldStructure.markIfCheap(visitor);
+    if (m_newStructure)
+        m_newStructure->markIfCheap(visitor);
+}
+
+bool PutByIdVariant::finalize()
+{
+    if (!m_oldStructure.isStillAlive())
+        return false;
+    if (m_newStructure && !Heap::isMarked(m_newStructure))
+        return false;
+    if (!m_conditionSet.areStillLive())
+        return false;
+    if (m_callLinkStatus && !m_callLinkStatus->finalize())
+        return false;
+    return true;
+}
+
 void PutByIdVariant::dump(PrintStream& out) const
 {
     dumpInContext(out, 0);
@@ -226,7 +311,7 @@ void PutByIdVariant::dumpInContext(PrintStream& out, DumpContext* context) const
         
     case Transition:
         out.print(
-            "<Transition: ", inContext(oldStructure(), context), " -> ",
+            "<Transition: ", inContext(oldStructure(), context), " to ",
             pointerDumpInContext(newStructure(), context), ", [",
             inContext(m_conditionSet, context), "], offset = ", offset(), ", ",
             inContext(requiredType(), context), ">");
index bda17bb..05a3a13 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014, 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2014-2018 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -73,23 +73,28 @@ public:
         return m_oldStructure;
     }
     
-    const StructureSet& structureSet() const
-    {
-        return structure();
-    }
-    
     const StructureSet& oldStructure() const
     {
         ASSERT(kind() == Transition || kind() == Replace || kind() == Setter);
         return m_oldStructure;
     }
     
+    const StructureSet& structureSet() const
+    {
+        return oldStructure();
+    }
+    
     StructureSet& oldStructure()
     {
         ASSERT(kind() == Transition || kind() == Replace || kind() == Setter);
         return m_oldStructure;
     }
     
+    StructureSet& structureSet()
+    {
+        return oldStructure();
+    }
+    
     Structure* oldStructureForTransition() const;
     
     Structure* newStructure() const
@@ -97,6 +102,8 @@ public:
         ASSERT(kind() == Transition);
         return m_newStructure;
     }
+    
+    void fixTransitionToReplaceIfNecessary();
 
     InferredType::Descriptor requiredType() const
     {
@@ -129,6 +136,9 @@ public:
 
     bool attemptToMerge(const PutByIdVariant& other);
     
+    void markIfCheap(SlotVisitor&);
+    bool finalize();
+    
     void dump(PrintStream&) const;
     void dumpInContext(PrintStream&, DumpContext*) const;
 
@@ -137,7 +147,7 @@ private:
     
     Kind m_kind;
     StructureSet m_oldStructure;
-    Structure* m_newStructure;
+    Structure* m_newStructure { nullptr };
     ObjectPropertyConditionSet m_conditionSet;
     PropertyOffset m_offset;
     InferredType::Descriptor m_requiredType;
diff --git a/Source/JavaScriptCore/bytecode/RecordedStatuses.cpp b/Source/JavaScriptCore/bytecode/RecordedStatuses.cpp
new file mode 100644 (file)
index 0000000..3d7e13e
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2018 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 "RecordedStatuses.h"
+
+namespace JSC {
+
+RecordedStatuses& RecordedStatuses::operator=(RecordedStatuses&& other)
+{
+    calls = WTFMove(other.calls);
+    gets = WTFMove(other.gets);
+    puts = WTFMove(other.puts);
+    ins = WTFMove(other.ins);
+    shrinkToFit();
+    return *this;
+}
+
+RecordedStatuses::RecordedStatuses(RecordedStatuses&& other)
+{
+    *this = WTFMove(other);
+}
+
+CallLinkStatus* RecordedStatuses::addCallLinkStatus(const CodeOrigin& codeOrigin, const CallLinkStatus& status)
+{
+    auto statusPtr = std::make_unique<CallLinkStatus>(status);
+    CallLinkStatus* result = statusPtr.get();
+    calls.append(std::make_pair(codeOrigin, WTFMove(statusPtr)));
+    return result;
+}
+
+GetByIdStatus* RecordedStatuses::addGetByIdStatus(const CodeOrigin& codeOrigin, const GetByIdStatus& status)
+{
+    auto statusPtr = std::make_unique<GetByIdStatus>(status);
+    GetByIdStatus* result = statusPtr.get();
+    gets.append(std::make_pair(codeOrigin, WTFMove(statusPtr)));
+    return result;
+}
+    
+PutByIdStatus* RecordedStatuses::addPutByIdStatus(const CodeOrigin& codeOrigin, const PutByIdStatus& status)
+{
+    auto statusPtr = std::make_unique<PutByIdStatus>(status);
+    PutByIdStatus* result = statusPtr.get();
+    puts.append(std::make_pair(codeOrigin, WTFMove(statusPtr)));
+    return result;
+}
+
+InByIdStatus* RecordedStatuses::addInByIdStatus(const CodeOrigin& codeOrigin, const InByIdStatus& status)
+{
+    auto statusPtr = std::make_unique<InByIdStatus>(status);
+    InByIdStatus* result = statusPtr.get();
+    ins.append(std::make_pair(codeOrigin, WTFMove(statusPtr)));
+    return result;
+}
+
+void RecordedStatuses::markIfCheap(SlotVisitor& slotVisitor)
+{
+    for (auto& pair : gets)
+        pair.second->markIfCheap(slotVisitor);
+    for (auto& pair : puts)
+        pair.second->markIfCheap(slotVisitor);
+    for (auto& pair : ins)
+        pair.second->markIfCheap(slotVisitor);
+}
+
+void RecordedStatuses::finalizeWithoutDeleting()
+{
+    // This variant of finalize gets called from within graph safepoints -- so there may be DFG IR in
+    // some compiler thread that points to the statuses. That thread is stopped at a safepoint so
+    // it's OK to edit its data structure, but it's not OK to delete them. Hence we don't remove
+    // anything from the vector or delete the unique_ptrs.
+    
+    auto finalize = [] (auto& vector) {
+        for (auto& pair : vector) {
+            if (!pair.second->finalize())
+                *pair.second = { };
+        }
+    };
+    forEachVector(finalize);
+}
+
+void RecordedStatuses::finalize()
+{
+    auto finalize = [] (auto& vector) {
+        vector.removeAllMatching(
+            [&] (auto& pair) -> bool {
+                return !*pair.second || !pair.second->finalize();
+            });
+        vector.shrinkToFit();
+    };
+    forEachVector(finalize);
+}
+
+void RecordedStatuses::shrinkToFit()
+{
+    forEachVector([] (auto& vector) { vector.shrinkToFit(); });
+}
+
+} // namespace JSC
+
diff --git a/Source/JavaScriptCore/bytecode/RecordedStatuses.h b/Source/JavaScriptCore/bytecode/RecordedStatuses.h
new file mode 100644 (file)
index 0000000..fc03bd2
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2018 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. 
+ */
+
+#pragma once
+
+#include "CallLinkStatus.h"
+#include "GetByIdStatus.h"
+#include "InByIdStatus.h"
+#include "PutByIdStatus.h"
+
+namespace JSC {
+
+struct RecordedStatuses {
+    RecordedStatuses() { }
+    
+    RecordedStatuses& operator=(const RecordedStatuses& other) = delete;
+    
+    RecordedStatuses& operator=(RecordedStatuses&& other);
+    
+    RecordedStatuses(const RecordedStatuses& other) = delete;
+    
+    RecordedStatuses(RecordedStatuses&& other);
+    
+    CallLinkStatus* addCallLinkStatus(const CodeOrigin&, const CallLinkStatus&);
+    GetByIdStatus* addGetByIdStatus(const CodeOrigin&, const GetByIdStatus&);
+    PutByIdStatus* addPutByIdStatus(const CodeOrigin&, const PutByIdStatus&);
+    InByIdStatus* addInByIdStatus(const CodeOrigin&, const InByIdStatus&);
+    
+    void markIfCheap(SlotVisitor& slotVisitor);
+    
+    void finalizeWithoutDeleting();
+    void finalize();
+    
+    void shrinkToFit();
+    
+    template<typename Func>
+    void forEachVector(const Func& func)
+    {
+        func(calls);
+        func(gets);
+        func(puts);
+        func(ins);
+    }
+    
+    Vector<std::pair<CodeOrigin, std::unique_ptr<CallLinkStatus>>> calls;
+    Vector<std::pair<CodeOrigin, std::unique_ptr<GetByIdStatus>>> gets;
+    Vector<std::pair<CodeOrigin, std::unique_ptr<PutByIdStatus>>> puts;
+    Vector<std::pair<CodeOrigin, std::unique_ptr<InByIdStatus>>> ins;
+};
+
+} // namespace JSC
+
index 2ccb8f0..20e616f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014, 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2014-2018 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
 
 namespace JSC {
 
+void StructureSet::markIfCheap(SlotVisitor& visitor) const
+{
+    for (Structure* structure : *this)
+        structure->markIfCheap(visitor);
+}
+
+bool StructureSet::isStillAlive() const
+{
+    for (Structure* structure : *this) {
+        if (!Heap::isMarked(structure))
+            return false;
+    }
+    return true;
+}
+
 void StructureSet::dumpInContext(PrintStream& out, DumpContext* context) const
 {
     CommaPrinter comma;
index 071b925..6c54895 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011, 2013-2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2011-2018 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -58,6 +58,9 @@ public:
     {
         return onlyEntry();
     }
+
+    void markIfCheap(SlotVisitor&) const;
+    bool isStillAlive() const;
     
     void dumpInContext(PrintStream&, DumpContext*) const;
     void dump(PrintStream&) const;
index 8351539..db200d9 100644 (file)
@@ -305,6 +305,39 @@ bool StructureStubInfo::propagateTransitions(SlotVisitor& visitor)
     return true;
 }
 
+StubInfoSummary StructureStubInfo::summary() const
+{
+    StubInfoSummary takesSlowPath = StubInfoSummary::TakesSlowPath;
+    StubInfoSummary simple = StubInfoSummary::Simple;
+    if (cacheType == CacheType::Stub) {
+        PolymorphicAccess* list = u.stub;
+        for (unsigned i = 0; i < list->size(); ++i) {
+            const AccessCase& access = list->at(i);
+            if (access.doesCalls()) {
+                takesSlowPath = StubInfoSummary::TakesSlowPathAndMakesCalls;
+                simple = StubInfoSummary::MakesCalls;
+                break;
+            }
+        }
+    }
+    
+    if (tookSlowPath || sawNonCell)
+        return takesSlowPath;
+    
+    if (!everConsidered)
+        return StubInfoSummary::NoInformation;
+    
+    return simple;
+}
+
+StubInfoSummary StructureStubInfo::summary(const StructureStubInfo* stubInfo)
+{
+    if (!stubInfo)
+        return StubInfoSummary::NoInformation;
+    
+    return stubInfo->summary();
+}
+
 bool StructureStubInfo::containsPC(void* pc) const
 {
     if (cacheType != CacheType::Stub)
index 133b604..6d03355 100644 (file)
@@ -35,6 +35,7 @@
 #include "Structure.h"
 #include "StructureSet.h"
 #include "StructureStubClearingWatchpoint.h"
+#include "StubInfoSummary.h"
 
 namespace JSC {
 
@@ -158,6 +159,10 @@ public:
         return false;
     }
 
+    StubInfoSummary summary() const;
+    
+    static StubInfoSummary summary(const StructureStubInfo*);
+
     bool containsPC(void* pc) const;
 
     CodeOrigin codeOrigin;
diff --git a/Source/JavaScriptCore/bytecode/StubInfoSummary.cpp b/Source/JavaScriptCore/bytecode/StubInfoSummary.cpp
new file mode 100644 (file)
index 0000000..634babe
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2018 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 "StubInfoSummary.h"
+
+#include <wtf/PrintStream.h>
+
+namespace WTF {
+
+void printInternal(PrintStream& out, JSC::StubInfoSummary summary)
+{
+    switch (summary) {
+    case JSC::StubInfoSummary::NoInformation:
+        out.print("NoInformation");
+        return;
+    case JSC::StubInfoSummary::Simple:
+        out.print("Simple");
+        return;
+    case JSC::StubInfoSummary::MakesCalls:
+        out.print("MakesCalls");
+        return;
+    case JSC::StubInfoSummary::TakesSlowPath:
+        out.print("TakesSlowPath");
+        return;
+    case JSC::StubInfoSummary::TakesSlowPathAndMakesCalls:
+        out.print("TakesSlowPathAndMakesCalls");
+        return;
+    }
+    RELEASE_ASSERT_NOT_REACHED();
+}
+
+} // namespace WTF
+
diff --git a/Source/JavaScriptCore/bytecode/StubInfoSummary.h b/Source/JavaScriptCore/bytecode/StubInfoSummary.h
new file mode 100644 (file)
index 0000000..010d918
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2018 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. 
+ */
+
+#pragma once
+
+namespace JSC {
+
+enum class StubInfoSummary : int8_t {
+    NoInformation,
+    Simple,
+    MakesCalls,
+    TakesSlowPath,
+    TakesSlowPathAndMakesCalls
+};
+
+inline bool isInlineable(StubInfoSummary summary)
+{
+    switch (summary) {
+    case StubInfoSummary::Simple:
+    case StubInfoSummary::MakesCalls:
+        return true;
+    case StubInfoSummary::NoInformation:
+    case StubInfoSummary::TakesSlowPath:
+    case StubInfoSummary::TakesSlowPathAndMakesCalls:
+        return false;
+    }
+    
+    RELEASE_ASSERT_NOT_REACHED();
+}
+
+inline StubInfoSummary slowVersion(StubInfoSummary summary)
+{
+    switch (summary) {
+    case StubInfoSummary::Simple:
+    case StubInfoSummary::NoInformation:
+    case StubInfoSummary::TakesSlowPath:
+        return StubInfoSummary::TakesSlowPath;
+    case StubInfoSummary::MakesCalls:
+    case StubInfoSummary::TakesSlowPathAndMakesCalls:
+        return StubInfoSummary::TakesSlowPathAndMakesCalls;
+    }
+    
+    RELEASE_ASSERT_NOT_REACHED();
+}
+
+} // namespace JSC
+
+namespace WTF {
+
+class PrintStream;
+void printInternal(PrintStream&, JSC::StubInfoSummary);
+
+} // namespace WTF
+
diff --git a/Source/JavaScriptCore/bytecode/TerminatedCodeOrigin.h b/Source/JavaScriptCore/bytecode/TerminatedCodeOrigin.h
new file mode 100644 (file)
index 0000000..9d01156
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2018 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. 
+ */
+
+#pragma once
+
+#include "CodeOrigin.h"
+
+namespace JSC {
+
+struct InlineCallFrame;
+
+struct TerminatedCodeOrigin {
+    TerminatedCodeOrigin() { }
+    
+    TerminatedCodeOrigin(const CodeOrigin& codeOrigin, InlineCallFrame* terminal)
+        : codeOrigin(codeOrigin)
+        , terminal(terminal)
+    {
+    }
+    
+    CodeOrigin codeOrigin;
+    InlineCallFrame* terminal { nullptr };
+};
+
+struct TerminatedCodeOriginHashTranslator {
+    static unsigned hash(const TerminatedCodeOrigin& value)
+    {
+        return value.codeOrigin.approximateHash(value.terminal);
+    }
+    
+    static bool equal(const CodeOrigin& a, const TerminatedCodeOrigin& b)
+    {
+        return b.codeOrigin.isApproximatelyEqualTo(a, b.terminal);
+    }
+};
+
+} // namespace JSC
+
index b19adb4..7fa5616 100644 (file)
@@ -203,3 +203,23 @@ void DeferredWatchpointFire::takeWatchpointsToFire(WatchpointSet* watchpointsToF
 
 } // namespace JSC
 
+namespace WTF {
+
+void printInternal(PrintStream& out, JSC::WatchpointState state)
+{
+    switch (state) {
+    case JSC::ClearWatchpoint:
+        out.print("ClearWatchpoint");
+        return;
+    case JSC::IsWatched:
+        out.print("IsWatched");
+        return;
+    case JSC::IsInvalidated:
+        out.print("IsInvalidated");
+        return;
+    }
+    RELEASE_ASSERT_NOT_REACHED();
+}
+
+} // namespace WTF
+
index caff418..18d8c4e 100644 (file)
@@ -473,3 +473,10 @@ private:
 };
 
 } // namespace JSC
+
+namespace WTF {
+
+void printInternal(PrintStream& out, JSC::WatchpointState);
+
+} // namespace WTF
+
index f4dfabb..fba1299 100644 (file)
@@ -212,6 +212,8 @@ public:
     
     PhiChildren* phiChildren() { return m_phiChildren.get(); }
     
+    void filterICStatus(Node*);
+    
 private:
     void clobberWorld();
     void didFoldClobberWorld();
index 83fd5c4..aec920b 100644 (file)
@@ -3613,6 +3613,10 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
     case LoopHint:
     case ZombieHint:
     case ExitOK:
+    case FilterCallLinkStatus:
+    case FilterGetByIdStatus:
+    case FilterPutByIdStatus:
+    case FilterInByIdStatus:
     case ClearCatchLocals:
         break;
 
@@ -3734,6 +3738,42 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
 }
 
 template<typename AbstractStateType>
+void AbstractInterpreter<AbstractStateType>::filterICStatus(Node* node)
+{
+    switch (node->op()) {
+    case FilterCallLinkStatus:
+        if (JSValue value = forNode(node->child1()).m_value)
+            node->callLinkStatus()->filter(m_vm, value);
+        break;
+        
+    case FilterGetByIdStatus: {
+        AbstractValue& value = forNode(node->child1());
+        if (value.m_structure.isFinite())
+            node->getByIdStatus()->filter(value.m_structure.toStructureSet());
+        break;
+    }
+        
+    case FilterInByIdStatus: {
+        AbstractValue& value = forNode(node->child1());
+        if (value.m_structure.isFinite())
+            node->inByIdStatus()->filter(value.m_structure.toStructureSet());
+        break;
+    }
+        
+    case FilterPutByIdStatus: {
+        AbstractValue& value = forNode(node->child1());
+        if (value.m_structure.isFinite())
+            node->putByIdStatus()->filter(value.m_structure.toStructureSet());
+        break;
+    }
+
+    default:
+        RELEASE_ASSERT_NOT_REACHED();
+        break;
+    }
+}
+
+template<typename AbstractStateType>
 bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned indexInBlock)
 {
     return executeEffects(indexInBlock, m_state.block()->at(indexInBlock));
index 968498d..ed35269 100644 (file)
@@ -400,6 +400,12 @@ private:
                     // butterfly's child and check if it's a candidate.
                     break;
                     
+                case FilterGetByIdStatus:
+                case FilterPutByIdStatus:
+                case FilterCallLinkStatus:
+                case FilterInByIdStatus:
+                    break;
+
                 case CheckArray:
                     escapeBasedOnArrayMode(node->arrayMode(), node->child1(), node);
                     break;
@@ -1182,7 +1188,11 @@ private:
                 }
                     
                 case CheckArray:
-                case GetButterfly: {
+                case GetButterfly:
+                case FilterGetByIdStatus:
+                case FilterPutByIdStatus:
+                case FilterCallLinkStatus:
+                case FilterInByIdStatus: {
                     if (!isEliminatedAllocation(node->child1().node()))
                         break;
                     node->remove(m_graph);
index d89f2af..e9c7fa5 100644 (file)
@@ -1083,9 +1083,8 @@ private:
         // code block had gathered.
         LazyOperandValueProfileParser m_lazyOperands;
         
-        CallLinkInfoMap m_callLinkInfos;
-        StubInfoMap m_stubInfos;
-        ByValInfoMap m_byValInfos;
+        ICStatusMap m_baselineMap;
+        ICStatusContext m_optimizedContext;
         
         // Pointers to the argument position trackers for this slice of code.
         Vector<ArgumentPosition*> m_argumentPositions;
@@ -1103,10 +1102,7 @@ private:
             InlineCallFrame::Kind,
             BasicBlock* continuationBlock);
         
-        ~InlineStackEntry()
-        {
-            m_byteCodeParser->m_inlineStackTop = m_caller;
-        }
+        ~InlineStackEntry();
         
         VirtualRegister remapOperand(VirtualRegister operand) const
         {
@@ -1121,6 +1117,8 @@ private:
     
     InlineStackEntry* m_inlineStackTop;
     
+    ICStatusContextStack m_icContextStack;
+    
     struct DelayedSetLocal {
         CodeOrigin m_origin;
         VirtualRegister m_operand;
@@ -1147,10 +1145,6 @@ private:
     
     Vector<DelayedSetLocal, 2> m_setLocalQueue;
 
-    CodeBlock* m_dfgCodeBlock;
-    CallLinkStatus::ContextMap m_callContextMap;
-    StubInfoMap m_dfgStubInfos;
-    
     Instruction* m_currentInstruction;
     bool m_hasDebuggerEnabled;
     bool m_hasAnyForceOSRExits { false };
@@ -1216,7 +1210,7 @@ ByteCodeParser::Terminality ByteCodeParser::handleCall(Instruction* pc, NodeType
 
     CallLinkStatus callLinkStatus = CallLinkStatus::computeFor(
         m_inlineStackTop->m_profiledBlock, currentCodeOrigin(),
-        m_inlineStackTop->m_callLinkInfos, m_callContextMap);
+        m_inlineStackTop->m_baselineMap, m_icContextStack);
 
     InlineCallFrame::Kind kind = InlineCallFrame::kindFor(callMode);
 
@@ -1244,6 +1238,8 @@ ByteCodeParser::Terminality ByteCodeParser::handleCall(
     // If we have profiling information about this call, and it did not behave too polymorphically,
     // we may be able to inline it, or in the case of recursive tail calls turn it into a jump.
     if (callLinkStatus.canOptimize()) {
+        addToGraph(FilterCallLinkStatus, OpInfo(m_graph.m_plan.recordedStatuses.addCallLinkStatus(currentCodeOrigin(), callLinkStatus)), callTarget);
+        
         VirtualRegister thisArgument = virtualRegisterForArgument(0, registerOffset);
         auto optimizationResult = handleInlining(callTarget, result, callLinkStatus, registerOffset, thisArgument,
             argumentCountIncludingThis, m_currentIndex + instructionSize, op, kind, prediction);
@@ -1281,12 +1277,14 @@ ByteCodeParser::Terminality ByteCodeParser::handleVarargsCall(Instruction* pc, N
     
     CallLinkStatus callLinkStatus = CallLinkStatus::computeFor(
         m_inlineStackTop->m_profiledBlock, currentCodeOrigin(),
-        m_inlineStackTop->m_callLinkInfos, m_callContextMap);
+        m_inlineStackTop->m_baselineMap, m_icContextStack);
     refineStatically(callLinkStatus, callTarget);
     
     VERBOSE_LOG("    Varargs call link status at ", currentCodeOrigin(), ": ", callLinkStatus, "\n");
     
     if (callLinkStatus.canOptimize()) {
+        addToGraph(FilterCallLinkStatus, OpInfo(m_graph.m_plan.recordedStatuses.addCallLinkStatus(currentCodeOrigin(), callLinkStatus)), callTarget);
+        
         if (handleVarargsInlining(callTarget, result,
             callLinkStatus, firstFreeReg, VirtualRegister(thisReg), VirtualRegister(arguments),
             firstVarArgOffset, op,
@@ -3332,9 +3330,12 @@ bool ByteCodeParser::handleDOMJITGetter(int resultOperand, const GetByIdVariant&
     if (!check(variant.conditionSet()))
         return false;
     addToGraph(CheckStructure, OpInfo(m_graph.addStructureSet(variant.structureSet())), thisNode);
-
+    
     // We do not need to emit CheckCell thingy here. When the custom accessor is replaced to different one, Structure transition occurs.
     addToGraph(CheckSubClass, OpInfo(domAttribute.classInfo), thisNode);
+    
+    bool wasSeenInJIT = true;
+    addToGraph(FilterGetByIdStatus, OpInfo(m_graph.m_plan.recordedStatuses.addGetByIdStatus(currentCodeOrigin(), GetByIdStatus(GetByIdStatus::Custom, wasSeenInJIT, variant))), thisNode);
 
     CallDOMGetterData* callDOMGetterData = m_graph.m_callDOMGetterData.add();
     callDOMGetterData->customAccessorGetter = variant.customAccessorGetter();
@@ -3366,6 +3367,8 @@ bool ByteCodeParser::handleModuleNamespaceLoad(int resultOperand, SpeculatedType
     if (m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, BadCell))
         return false;
     addToGraph(CheckCell, OpInfo(m_graph.freeze(getById.moduleNamespaceObject())), Edge(base, CellUse));
+    
+    addToGraph(FilterGetByIdStatus, OpInfo(m_graph.m_plan.recordedStatuses.addGetByIdStatus(currentCodeOrigin(), getById)), base);
 
     // Ideally we wouldn't have to do this Phantom. But:
     //
@@ -4021,14 +4024,21 @@ void ByteCodeParser::handleGetById(
         return;
     }
     
+    // FIXME: If we use the GetByIdStatus for anything then we should record it and insert a node
+    // after everything else (like the GetByOffset or whatever) that will filter the recorded
+    // GetByIdStatus. That means that the constant folder also needs to do the same!
+    
     if (getByIdStatus.numVariants() > 1) {
         if (getByIdStatus.makesCalls() || !isFTL(m_graph.m_plan.mode)
-            || !Options::usePolymorphicAccessInlining()) {
+            || !Options::usePolymorphicAccessInlining()
+            || getByIdStatus.numVariants() > Options::maxPolymorphicAccessInliningListSize()) {
             set(VirtualRegister(destinationOperand),
                 addToGraph(getById, OpInfo(identifierNumber), OpInfo(prediction), base));
             return;
         }
         
+        addToGraph(FilterGetByIdStatus, OpInfo(m_graph.m_plan.recordedStatuses.addGetByIdStatus(currentCodeOrigin(), getByIdStatus)), base);
+        
         Vector<MultiGetByOffsetCase, 2> cases;
         
         // 1) Emit prototype structure checks for all chains. This could sort of maybe not be
@@ -4070,10 +4080,12 @@ void ByteCodeParser::handleGetById(
             addToGraph(MultiGetByOffset, OpInfo(data), OpInfo(prediction), base));
         return;
     }
+
+    addToGraph(FilterGetByIdStatus, OpInfo(m_graph.m_plan.recordedStatuses.addGetByIdStatus(currentCodeOrigin(), getByIdStatus)), base);
     
     ASSERT(getByIdStatus.numVariants() == 1);
     GetByIdVariant variant = getByIdStatus[0];
-                
+    
     Node* loadedValue = load(prediction, base, identifierNumber, variant);
     if (!loadedValue) {
         set(VirtualRegister(destinationOperand),
@@ -4164,7 +4176,8 @@ void ByteCodeParser::handlePutById(
     
     if (putByIdStatus.numVariants() > 1) {
         if (!isFTL(m_graph.m_plan.mode) || putByIdStatus.makesCalls()
-            || !Options::usePolymorphicAccessInlining()) {
+            || !Options::usePolymorphicAccessInlining()
+            || putByIdStatus.numVariants() > Options::maxPolymorphicAccessInliningListSize()) {
             emitPutById(base, identifierNumber, value, putByIdStatus, isDirect);
             return;
         }
@@ -4182,6 +4195,8 @@ void ByteCodeParser::handlePutById(
         
         if (UNLIKELY(m_graph.compilation()))
             m_graph.compilation()->noticeInlinedPutById();
+        
+        addToGraph(FilterPutByIdStatus, OpInfo(m_graph.m_plan.recordedStatuses.addPutByIdStatus(currentCodeOrigin(), putByIdStatus)), base);
 
         for (const PutByIdVariant& variant : putByIdStatus.variants()) {
             m_graph.registerInferredType(variant.requiredType());
@@ -4203,6 +4218,8 @@ void ByteCodeParser::handlePutById(
     
     switch (variant.kind()) {
     case PutByIdVariant::Replace: {
+        addToGraph(FilterPutByIdStatus, OpInfo(m_graph.m_plan.recordedStatuses.addPutByIdStatus(currentCodeOrigin(), putByIdStatus)), base);
+        
         store(base, identifierNumber, variant, value);
         if (UNLIKELY(m_graph.compilation()))
             m_graph.compilation()->noticeInlinedPutById();
@@ -4210,6 +4227,8 @@ void ByteCodeParser::handlePutById(
     }
     
     case PutByIdVariant::Transition: {
+        addToGraph(FilterPutByIdStatus, OpInfo(m_graph.m_plan.recordedStatuses.addPutByIdStatus(currentCodeOrigin(), putByIdStatus)), base);
+        
         addToGraph(CheckStructure, OpInfo(m_graph.addStructureSet(variant.oldStructure())), base);
         if (!check(variant.conditionSet())) {
             emitPutById(base, identifierNumber, value, putByIdStatus, isDirect);
@@ -4277,6 +4296,8 @@ void ByteCodeParser::handlePutById(
     }
         
     case PutByIdVariant::Setter: {
+        addToGraph(FilterPutByIdStatus, OpInfo(m_graph.m_plan.recordedStatuses.addPutByIdStatus(currentCodeOrigin(), putByIdStatus)), base);
+        
         Node* loadedValue = load(SpecCellOther, base, identifierNumber, variant);
         if (!loadedValue) {
             emitPutById(base, identifierNumber, value, putByIdStatus, isDirect);
@@ -4513,6 +4534,7 @@ void ByteCodeParser::parseBlock(unsigned limit)
 
                             m_graph.freeze(rareData);
                             m_graph.watchpoints().addLazily(rareData->allocationProfileWatchpointSet());
+                            
                             // The callee is still live up to this point.
                             addToGraph(Phantom, callee);
                             Node* object = addToGraph(NewObject, OpInfo(m_graph.registerStructure(structure)));
@@ -4808,7 +4830,7 @@ void ByteCodeParser::parseBlock(unsigned limit)
             auto& bytecode = *reinterpret_cast<OpInstanceof*>(currentInstruction);
             
             InstanceOfStatus status = InstanceOfStatus::computeFor(
-                m_inlineStackTop->m_profiledBlock, m_inlineStackTop->m_stubInfos,
+                m_inlineStackTop->m_profiledBlock, m_inlineStackTop->m_baselineMap,
                 m_currentIndex);
             
             Node* value = get(VirtualRegister(bytecode.value()));
@@ -5047,7 +5069,7 @@ void ByteCodeParser::parseBlock(unsigned limit)
             unsigned identifierNumber = 0;
             {
                 ConcurrentJSLocker locker(m_inlineStackTop->m_profiledBlock->m_lock);
-                ByValInfo* byValInfo = m_inlineStackTop->m_byValInfos.get(CodeOrigin(currentCodeOrigin().bytecodeIndex));
+                ByValInfo* byValInfo = m_inlineStackTop->m_baselineMap.get(CodeOrigin(currentCodeOrigin().bytecodeIndex)).byValInfo;
                 // FIXME: When the bytecode is not compiled in the baseline JIT, byValInfo becomes null.
                 // At that time, there is no information.
                 if (byValInfo
@@ -5116,7 +5138,7 @@ void ByteCodeParser::parseBlock(unsigned limit)
                 PutByIdStatus putByIdStatus;
                 {
                     ConcurrentJSLocker locker(m_inlineStackTop->m_profiledBlock->m_lock);
-                    ByValInfo* byValInfo = m_inlineStackTop->m_byValInfos.get(CodeOrigin(currentCodeOrigin().bytecodeIndex));
+                    ByValInfo* byValInfo = m_inlineStackTop->m_baselineMap.get(CodeOrigin(currentCodeOrigin().bytecodeIndex)).byValInfo;
                     // FIXME: When the bytecode is not compiled in the baseline JIT, byValInfo becomes null.
                     // At that time, there is no information.
                     if (byValInfo 
@@ -5222,8 +5244,8 @@ void ByteCodeParser::parseBlock(unsigned limit)
             
             UniquedStringImpl* uid = m_graph.identifiers()[identifierNumber];
             GetByIdStatus getByIdStatus = GetByIdStatus::computeFor(
-                m_inlineStackTop->m_profiledBlock, m_dfgCodeBlock,
-                m_inlineStackTop->m_stubInfos, m_dfgStubInfos,
+                m_inlineStackTop->m_profiledBlock,
+                m_inlineStackTop->m_baselineMap, m_icContextStack,
                 currentCodeOrigin(), uid);
 
             AccessType type = AccessType::Get;
@@ -5266,8 +5288,8 @@ void ByteCodeParser::parseBlock(unsigned limit)
             bool direct = currentInstruction[8].u.putByIdFlags & PutByIdIsDirect;
 
             PutByIdStatus putByIdStatus = PutByIdStatus::computeFor(
-                m_inlineStackTop->m_profiledBlock, m_dfgCodeBlock,
-                m_inlineStackTop->m_stubInfos, m_dfgStubInfos,
+                m_inlineStackTop->m_profiledBlock,
+                m_inlineStackTop->m_baselineMap, m_icContextStack,
                 currentCodeOrigin(), m_graph.identifiers()[identifierNumber]);
             
             handlePutById(base, identifierNumber, value, putByIdStatus, direct);
@@ -6451,8 +6473,8 @@ void ByteCodeParser::parseBlock(unsigned limit)
             UniquedStringImpl* uid = m_graph.identifiers()[identifierNumber];
 
             InByIdStatus status = InByIdStatus::computeFor(
-                m_inlineStackTop->m_profiledBlock, m_dfgCodeBlock,
-                m_inlineStackTop->m_stubInfos, m_dfgStubInfos,
+                m_inlineStackTop->m_profiledBlock,
+                m_inlineStackTop->m_baselineMap, m_icContextStack,
                 currentCodeOrigin(), uid);
 
             if (status.isSimple()) {
@@ -6473,6 +6495,8 @@ void ByteCodeParser::parseBlock(unsigned limit)
                 }
 
                 if (allOK) {
+                    addToGraph(FilterInByIdStatus, OpInfo(m_graph.m_plan.recordedStatuses.addInByIdStatus(currentCodeOrigin(), status)), base);
+                    
                     Node* match = addToGraph(MatchStructure, OpInfo(data), base);
                     set(VirtualRegister(currentInstruction[1].u.operand), match);
                     NEXT_OPCODE(op_in_by_id);
@@ -6657,13 +6681,18 @@ ByteCodeParser::InlineStackEntry::InlineStackEntry(
         // We do this while holding the lock because we want to encourage StructureStubInfo's
         // to be potentially added to operations and because the profiled block could be in the
         // middle of LLInt->JIT tier-up in which case we would be adding the info's right now.
-        if (m_profiledBlock->hasBaselineJITProfiling()) {
-            m_profiledBlock->getStubInfoMap(locker, m_stubInfos);
-            m_profiledBlock->getCallLinkInfoMap(locker, m_callLinkInfos);
-            m_profiledBlock->getByValInfoMap(locker, m_byValInfos);
-        }
+        if (m_profiledBlock->hasBaselineJITProfiling())
+            m_profiledBlock->getICStatusMap(locker, m_baselineMap);
     }
     
+    CodeBlock* optimizedBlock = m_profiledBlock->replacement();
+    m_optimizedContext.optimizedCodeBlock = optimizedBlock;
+    if (Options::usePolyvariantDevirtualization() && optimizedBlock) {
+        ConcurrentJSLocker locker(optimizedBlock->m_lock);
+        optimizedBlock->getICStatusMap(locker, m_optimizedContext.map);
+    }
+    byteCodeParser->m_icContextStack.append(&m_optimizedContext);
+    
     int argumentCountIncludingThisWithFixup = std::max<int>(argumentCountIncludingThis, codeBlock->numParameters());
 
     if (m_caller) {
@@ -6672,6 +6701,7 @@ ByteCodeParser::InlineStackEntry::InlineStackEntry(
         ASSERT(inlineCallFrameStart.isValid());
         
         m_inlineCallFrame = byteCodeParser->m_graph.m_plan.inlineCallFrames->add();
+        m_optimizedContext.inlineCallFrame = m_inlineCallFrame;
 
         // The owner is the machine code block, and we already have a barrier on that when the
         // plan finishes.
@@ -6727,6 +6757,13 @@ ByteCodeParser::InlineStackEntry::InlineStackEntry(
     byteCodeParser->m_inlineStackTop = this;
 }
 
+ByteCodeParser::InlineStackEntry::~InlineStackEntry()
+{
+    m_byteCodeParser->m_inlineStackTop = m_caller;
+    RELEASE_ASSERT(m_byteCodeParser->m_icContextStack.last() == &m_optimizedContext);
+    m_byteCodeParser->m_icContextStack.removeLast();
+}
+
 void ByteCodeParser::parseCodeBlock()
 {
     clearCaches();
@@ -6823,15 +6860,6 @@ void ByteCodeParser::parse()
     
     VERBOSE_LOG("Parsing ", *m_codeBlock, "\n");
     
-    m_dfgCodeBlock = m_graph.m_plan.profiledDFGCodeBlock;
-    if (isFTL(m_graph.m_plan.mode) && m_dfgCodeBlock
-        && Options::usePolyvariantDevirtualization()) {
-        if (Options::usePolyvariantCallInlining())
-            CallLinkStatus::computeDFGStatuses(m_dfgCodeBlock, m_callContextMap);
-        if (Options::usePolyvariantByIdInlining())
-            m_dfgCodeBlock->getStubInfoMap(m_dfgStubInfos);
-    }
-    
     InlineStackEntry inlineStackEntry(
         this, m_codeBlock, m_profiledBlock, 0, VirtualRegister(), VirtualRegister(),
         m_codeBlock->numParameters(), InlineCallFrame::Call, nullptr);
index 6c58663..2b04fff 100644 (file)
@@ -453,6 +453,10 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
     case ProfileControlFlow:
     case PutHint:
     case InitializeEntrypointArguments:
+    case FilterCallLinkStatus:
+    case FilterGetByIdStatus:
+    case FilterPutByIdStatus:
+    case FilterInByIdStatus:
         write(SideState);
         return;
         
index 2e4c963..facdbfb 100644 (file)
@@ -75,6 +75,10 @@ bool clobbersExitState(Graph& graph, Node* node)
     case FencedStoreBarrier:
     case AllocatePropertyStorage:
     case ReallocatePropertyStorage:
+    case FilterCallLinkStatus:
+    case FilterGetByIdStatus:
+    case FilterPutByIdStatus:
+    case FilterInByIdStatus:
         // 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
index 3d887e3..f7dbe3e 100644 (file)
@@ -35,6 +35,7 @@
 #include "InlineCallFrameSet.h"
 #include "JSCast.h"
 #include "ProfilerCompilation.h"
+#include "RecordedStatuses.h"
 #include <wtf/Bag.h>
 #include <wtf/Noncopyable.h>
 
@@ -125,6 +126,7 @@ public:
     Bag<CodeBlockJettisoningWatchpoint> watchpoints;
     Bag<AdaptiveStructureWatchpoint> adaptiveStructureWatchpoints;
     Bag<AdaptiveInferredPropertyValueWatchpoint> adaptiveInferredPropertyValueWatchpoints;
+    RecordedStatuses recordedStatuses;
     Vector<JumpReplacement> jumpReplacements;
     
     ScratchBuffer* catchOSREntryBuffer;
index 74cf85d..108180d 100644 (file)
@@ -557,7 +557,15 @@ private:
                     }
                 }
                 
+                auto addFilterStatus = [&] () {
+                    m_insertionSet.insertNode(
+                        indexInBlock, SpecNone, FilterGetByIdStatus, node->origin,
+                        OpInfo(m_graph.m_plan.recordedStatuses.addGetByIdStatus(node->origin.semantic, status)),
+                        Edge(child));
+                };
+                
                 if (status.numVariants() == 1) {
+                    addFilterStatus();
                     emitGetByOffset(indexInBlock, node, baseValue, status[0], identifierNumber);
                     changed = true;
                     break;
@@ -566,6 +574,7 @@ private:
                 if (!isFTL(m_graph.m_plan.mode))
                     break;
                 
+                addFilterStatus();
                 MultiGetByOffsetData* data = m_graph.m_multiGetByOffsetData.add();
                 for (const GetByIdVariant& variant : status.variants()) {
                     data->cases.append(
@@ -639,6 +648,11 @@ private:
                 if (!allGood)
                     break;
                 
+                m_insertionSet.insertNode(
+                    indexInBlock, SpecNone, FilterPutByIdStatus, node->origin,
+                    OpInfo(m_graph.m_plan.recordedStatuses.addPutByIdStatus(node->origin.semantic, status)),
+                    Edge(child));
+                
                 if (status.numVariants() == 1) {
                     emitPutByOffset(indexInBlock, node, baseValue, status[0], identifierNumber);
                     break;
index 4e1008b..f555da2 100644 (file)
@@ -46,7 +46,10 @@ struct SetPointerAdaptor {
     {
         return set->add(common.watchpoints.add(codeBlock));
     }
-    static bool hasBeenInvalidated(T set) { return set->hasBeenInvalidated(); }
+    static bool hasBeenInvalidated(T set)
+    {
+        return set->hasBeenInvalidated();
+    }
     static void dumpInContext(PrintStream& out, T set, DumpContext*)
     {
         out.print(RawPointer(set));
index d2333da..789483f 100644 (file)
@@ -314,6 +314,10 @@ bool doesGC(Graph& graph, Node* node)
     case AtomicsXor:
     case AtomicsIsLockFree:
     case MatchStructure:
+    case FilterCallLinkStatus:
+    case FilterGetByIdStatus:
+    case FilterPutByIdStatus:
+    case FilterInByIdStatus:
         return false;
 
     case PushWithScope:
index 0949f6f..2f0e23e 100644 (file)
@@ -2183,6 +2183,10 @@ private:
         case GetGlobalThis:
         case ExtractValueFromWeakMapGet:
         case CPUIntrinsic:
+        case FilterCallLinkStatus:
+        case FilterGetByIdStatus:
+        case FilterPutByIdStatus:
+        case FilterInByIdStatus:
             break;
 #else
         default:
index c4927fa..ca4b0e2 100644 (file)
@@ -356,6 +356,14 @@ void Graph::dump(PrintStream& out, const char* prefix, Node* node, DumpContext*
         out.print(comma, "ignoreLastIndexIsWritable = ", node->ignoreLastIndexIsWritable());
     if (node->isConstant())
         out.print(comma, pointerDumpInContext(node->constant(), context));
+    if (node->hasCallLinkStatus())
+        out.print(comma, *node->callLinkStatus());
+    if (node->hasGetByIdStatus())
+        out.print(comma, *node->getByIdStatus());
+    if (node->hasInByIdStatus())
+        out.print(comma, *node->inByIdStatus());
+    if (node->hasPutByIdStatus())
+        out.print(comma, *node->putByIdStatus());
     if (node->isJump())
         out.print(comma, "T:", *node->targetBlock());
     if (node->isBranch())
index 3c91e47..11d90d1 100644 (file)
@@ -102,6 +102,10 @@ ExitMode mayExitImpl(Graph& graph, Node* node, StateType& state)
     case PutClosureVar:
     case RecordRegExpCachedResult:
     case NukeStructureAndSetButterfly:
+    case FilterCallLinkStatus:
+    case FilterGetByIdStatus:
+    case FilterPutByIdStatus:
+    case FilterInByIdStatus:
         break;
 
     case StrCat:
index eee5e77..b042993 100644 (file)
@@ -2769,6 +2769,50 @@ public:
         ASSERT(op() == ThrowStaticError);
         return m_opInfo.as<uint32_t>();
     }
+    
+    bool hasCallLinkStatus()
+    {
+        return op() == FilterCallLinkStatus;
+    }
+    
+    CallLinkStatus* callLinkStatus()
+    {
+        ASSERT(hasCallLinkStatus());
+        return m_opInfo.as<CallLinkStatus*>();
+    }
+    
+    bool hasGetByIdStatus()
+    {
+        return op() == FilterGetByIdStatus;
+    }
+    
+    GetByIdStatus* getByIdStatus()
+    {
+        ASSERT(hasGetByIdStatus());
+        return m_opInfo.as<GetByIdStatus*>();
+    }
+    
+    bool hasInByIdStatus()
+    {
+        return op() == FilterInByIdStatus;
+    }
+    
+    InByIdStatus* inByIdStatus()
+    {
+        ASSERT(hasInByIdStatus());
+        return m_opInfo.as<InByIdStatus*>();
+    }
+    
+    bool hasPutByIdStatus()
+    {
+        return op() == FilterPutByIdStatus;
+    }
+    
+    PutByIdStatus* putByIdStatus()
+    {
+        ASSERT(hasPutByIdStatus());
+        return m_opInfo.as<PutByIdStatus*>();
+    }
 
     void dumpChildren(PrintStream& out)
     {
index a50e520..14edfdb 100644 (file)
@@ -399,11 +399,8 @@ namespace JSC { namespace DFG {
     macro(GetArgument, NodeResultJS) \
     \
     macro(NewFunction, NodeResultJS) \
-    \
     macro(NewGeneratorFunction, NodeResultJS) \
-    \
     macro(NewAsyncGeneratorFunction, NodeResultJS) \
-    \
     macro(NewAsyncFunction, NodeResultJS) \
     \
     /* Block terminals. */\
@@ -480,6 +477,12 @@ namespace JSC { namespace DFG {
     \
     /* Used for $vm performance debugging */ \
     macro(CPUIntrinsic, NodeResultJS | NodeMustGenerate) \
+    \
+    /* Used to provide feedback to the IC profiler. */ \
+    macro(FilterCallLinkStatus, NodeMustGenerate) \
+    macro(FilterGetByIdStatus, NodeMustGenerate) \
+    macro(FilterInByIdStatus, NodeMustGenerate) \
+    macro(FilterPutByIdStatus, NodeMustGenerate) \
 
 
 // This enum generates a monotonically increasing id for all Node types,
index d719f9f..1532aa3 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013, 2016 Apple Inc. All rights reserved.
+ * Copyright (C) 2013-2018 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -42,11 +42,17 @@ void OSRExitBase::considerAddingAsFrequentExitSiteSlow(CodeBlock* profiledCodeBl
         baselineCodeBlockForOriginAndBaselineCodeBlock(
             m_codeOriginForExitProfile, profiledCodeBlock);
     if (sourceProfiledCodeBlock) {
+        ExitingInlineKind inlineKind;
+        if (m_codeOriginForExitProfile.inlineCallFrame)
+            inlineKind = ExitFromInlined;
+        else
+            inlineKind = ExitFromNotInlined;
+        
         FrequentExitSite site;
         if (m_wasHoisted)
-            site = FrequentExitSite(HoistingFailed, jitType);
+            site = FrequentExitSite(HoistingFailed, jitType, inlineKind);
         else
-            site = FrequentExitSite(m_codeOriginForExitProfile.bytecodeIndex, m_kind, jitType);
+            site = FrequentExitSite(m_codeOriginForExitProfile.bytecodeIndex, m_kind, jitType, inlineKind);
         ExitProfile::add(sourceProfiledCodeBlock, site);
     }
 }
index 1ceb201..ee11fdc 100644 (file)
@@ -756,6 +756,7 @@ private:
         }
 
         promoteLocalHeap();
+        removeICStatusFilters();
 
         if (Options::validateGraphAtEachPhase())
             DFG::validate(m_graph, DumpGraph, graphBeforeSinking);
@@ -1088,6 +1089,12 @@ private:
         case PutHint:
             // Handled by OSR availability analysis
             break;
+            
+        case FilterCallLinkStatus:
+        case FilterGetByIdStatus:
+        case FilterPutByIdStatus:
+        case FilterInByIdStatus:
+            break;
 
         default:
             m_graph.doToChildren(
@@ -2331,6 +2338,25 @@ private:
 
         RELEASE_ASSERT_NOT_REACHED();
     }
+    
+    void removeICStatusFilters()
+    {
+        for (BasicBlock* block : m_graph.blocksInNaturalOrder()) {
+            for (Node* node : *block) {
+                switch (node->op()) {
+                case FilterCallLinkStatus:
+                case FilterGetByIdStatus:
+                case FilterPutByIdStatus:
+                case FilterInByIdStatus:
+                    if (node->child1()->isPhantomAllocation())
+                        node->removeWithoutChecks();
+                    break;
+                default:
+                    break;
+                }
+            }
+        }
+    }
 
     // This is a great way of asking value->isStillValid() without having to worry about getting
     // different answers. It turns out that this analysis works OK regardless of what this
index 0ef21f6..a2674fc 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013-2017 Apple Inc. All rights reserved.
+ * Copyright (C) 2013-2018 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -555,6 +555,7 @@ void Plan::reallyAdd(CommonData* commonData)
     identifiers.reallyAdd(*vm, commonData);
     weakReferences.reallyAdd(*vm, commonData);
     transitions.reallyAdd(*vm, commonData);
+    commonData->recordedStatuses = WTFMove(recordedStatuses);
 }
 
 void Plan::notifyCompiling()
@@ -633,6 +634,8 @@ void Plan::checkLivenessAndVisitChildren(SlotVisitor& visitor)
     cleanMustHandleValuesIfNecessary();
     for (unsigned i = mustHandleValues.size(); i--;)
         visitor.appendUnbarriered(mustHandleValues[i]);
+    
+    recordedStatuses.markIfCheap(visitor);
 
     visitor.appendUnbarriered(codeBlock);
     visitor.appendUnbarriered(codeBlock->alternative());
@@ -649,6 +652,11 @@ void Plan::checkLivenessAndVisitChildren(SlotVisitor& visitor)
     transitions.visitChildren(visitor);
 }
 
+void Plan::finalizeInGC()
+{
+    recordedStatuses.finalizeWithoutDeleting();
+}
+
 bool Plan::isKnownToBeLiveDuringGC()
 {
     if (stage == Cancelled)
index a868597..ef4ab5c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013-2017 Apple Inc. All rights reserved.
+ * Copyright (C) 2013-2018 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -36,6 +36,7 @@
 #include "DeferredCompilationCallback.h"
 #include "Operands.h"
 #include "ProfilerCompilation.h"
+#include "RecordedStatuses.h"
 #include <wtf/HashMap.h>
 #include <wtf/ThreadSafeRefCounted.h>
 
@@ -72,6 +73,7 @@ struct Plan : public ThreadSafeRefCounted<Plan> {
     void iterateCodeBlocksForGC(const Func&);
     void checkLivenessAndVisitChildren(SlotVisitor&);
     bool isKnownToBeLiveDuringGC();
+    void finalizeInGC();
     void cancel();
 
     bool canTierUpAndOSREnter() const { return !tierUpAndOSREnterBytecodes.isEmpty(); }
@@ -104,6 +106,7 @@ struct Plan : public ThreadSafeRefCounted<Plan> {
     DesiredIdentifiers identifiers;
     DesiredWeakReferences weakReferences;
     DesiredTransitions transitions;
+    RecordedStatuses recordedStatuses;
     
     bool willTryToTierUp { false };
 
index e0dd115..2db3e64 100644 (file)
@@ -1214,6 +1214,10 @@ private:
         case InitializeEntrypointArguments:
         case WeakSetAdd:
         case WeakMapSet:
+        case FilterCallLinkStatus:
+        case FilterGetByIdStatus:
+        case FilterPutByIdStatus:
+        case FilterInByIdStatus:
         case ClearCatchLocals:
             break;
             
index cf3d755..43eb97b 100644 (file)
@@ -486,6 +486,14 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node, bool igno
         // already force these things to be ordered precisely. I'm just not confident enough in my
         // effect based memory model to rely solely on that right now.
         return false;
+        
+    case FilterCallLinkStatus:
+    case FilterGetByIdStatus:
+    case FilterPutByIdStatus:
+    case FilterInByIdStatus:
+        // We don't want these to be moved anywhere other than where we put them, since we want them
+        // to capture "profiling" at the point in control flow here the user put them.
+        return false;
 
     case GetByVal:
     case GetIndexedPropertyStorage:
index 2dc247e..4abf39b 100644 (file)
@@ -4046,6 +4046,14 @@ void SpeculativeJIT::compile(Node* node)
     case CheckStructureOrEmpty:
         DFG_CRASH(m_jit.graph(), node, "CheckStructureOrEmpty only used in 64-bit DFG");
         break;
+        
+    case FilterCallLinkStatus:
+    case FilterGetByIdStatus:
+    case FilterPutByIdStatus:
+    case FilterInByIdStatus:
+        m_interpreter.filterICStatus(node);
+        noResult(node);
+        break;
 
     case LastNodeType:
     case Phi:
index 5792594..af050fd 100644 (file)
@@ -4646,6 +4646,14 @@ void SpeculativeJIT::compile(Node* node)
         break;
 #endif // ENABLE(FTL_JIT)
 
+    case FilterCallLinkStatus:
+    case FilterGetByIdStatus:
+    case FilterPutByIdStatus:
+    case FilterInByIdStatus:
+        m_interpreter.filterICStatus(node);
+        noResult(node);
+        break;
+
     case LastNodeType:
     case EntrySwitch:
     case InitializeEntrypointArguments:
index 0b66754..9ab4e53 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013-2016 Apple Inc. All rights reserved.
+ * Copyright (C) 2013-2018 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -898,10 +898,14 @@ private:
         case TailCall: {
             ExecutableBase* executable = nullptr;
             Edge callee = m_graph.varArgChild(m_node, 0);
-            if (JSFunction* function = callee->dynamicCastConstant<JSFunction*>(vm()))
+            CallVariant callVariant;
+            if (JSFunction* function = callee->dynamicCastConstant<JSFunction*>(vm())) {
                 executable = function->executable();
-            else if (callee->isFunctionAllocation())
+                callVariant = CallVariant(function);
+            } else if (callee->isFunctionAllocation()) {
                 executable = callee->castOperand<FunctionExecutable*>();
+                callVariant = CallVariant(executable);
+            }
             
             if (!executable)
                 break;
@@ -919,6 +923,8 @@ private:
                 }
             }
             
+            m_graph.m_plan.recordedStatuses.addCallLinkStatus(m_node->origin.semantic, CallLinkStatus(callVariant));
+            
             m_node->convertToDirectCall(m_graph.freeze(executable));
             m_changed = true;
             break;
index 5367ef6..3a37367 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2015-2018 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -193,7 +193,13 @@ private:
                     return;
                 break;
             }
-            
+                
+            case FilterGetByIdStatus:
+            case FilterPutByIdStatus:
+            case FilterCallLinkStatus:
+            case FilterInByIdStatus:
+                break;
+
             case GetByOffset: {
                 if (node->child1()->op() == GetButterfly
                     && candidateButterflies.contains(node->child1().node())
@@ -382,6 +388,14 @@ private:
                 break;
             }
 
+            case FilterGetByIdStatus:
+            case FilterPutByIdStatus:
+            case FilterCallLinkStatus:
+            case FilterInByIdStatus:
+                if (node->child1().node() == candidate)
+                    node->remove(m_graph);
+                break;
+
             case GetByOffset: {
                 if (node->child2() == candidate) {
                     ASSERT(candidateButterflies.contains(node->child1().node())); // It's no longer a GetButterfly node, but it should've been a candidate butterfly.
index daf8544..68f6aac 100644 (file)
@@ -403,8 +403,10 @@ void Worklist::removeDeadPlans(VM& vm)
             Plan* plan = iter->value.get();
             if (plan->vm != &vm)
                 continue;
-            if (plan->isKnownToBeLiveDuringGC())
+            if (plan->isKnownToBeLiveDuringGC()) {
+                plan->finalizeInGC();
                 continue;
+            }
             RELEASE_ASSERT(plan->stage != Plan::Cancelled); // Should not be cancelled, yet.
             ASSERT(!deadPlanKeys.contains(plan->key()));
             deadPlanKeys.add(plan->key());
index 5f450d1..8c24a44 100644 (file)
@@ -56,6 +56,8 @@ namespace JSC { namespace FTL {
     macro(DirectArguments_minCapacity, DirectArguments::offsetOfMinCapacity()) \
     macro(DirectArguments_mappedArguments, DirectArguments::offsetOfMappedArguments()) \
     macro(DirectArguments_modifiedArgumentsDescriptor, DirectArguments::offsetOfModifiedArgumentsDescriptor()) \
+    macro(FunctionRareData_allocator, FunctionRareData::offsetOfObjectAllocationProfile() + ObjectAllocationProfile::offsetOfAllocator()) \
+    macro(FunctionRareData_structure, FunctionRareData::offsetOfObjectAllocationProfile() + ObjectAllocationProfile::offsetOfStructure()) \
     macro(GetterSetter_getter, GetterSetter::offsetOfGetter()) \
     macro(GetterSetter_setter, GetterSetter::offsetOfSetter()) \
     macro(JSArrayBufferView_length, JSArrayBufferView::offsetOfLength()) \
@@ -112,10 +114,10 @@ namespace JSC { namespace FTL {
     macro(StringImpl_length, StringImpl::lengthMemoryOffset()) \
     macro(Structure_classInfo, Structure::classInfoOffset()) \
     macro(Structure_globalObject, Structure::globalObjectOffset()) \
+    macro(Structure_indexingModeIncludingHistory, Structure::indexingModeIncludingHistoryOffset()) \
+    macro(Structure_inlineCapacity, Structure::inlineCapacityOffset()) \
     macro(Structure_prototype, Structure::prototypeOffset()) \
     macro(Structure_structureID, Structure::structureIDOffset()) \
-    macro(Structure_inlineCapacity, Structure::inlineCapacityOffset()) \
-    macro(Structure_indexingModeIncludingHistory, Structure::indexingModeIncludingHistoryOffset()) \
     macro(HashMapImpl_capacity, HashMapImpl<HashMapBucket<HashMapBucketDataKey>>::offsetOfCapacity()) \
     macro(HashMapImpl_buffer,  HashMapImpl<HashMapBucket<HashMapBucketDataKey>>::offsetOfBuffer()) \
     macro(HashMapImpl_head,  HashMapImpl<HashMapBucket<HashMapBucketDataKey>>::offsetOfHead()) \
index d8a584f..1051563 100644 (file)
@@ -354,6 +354,11 @@ inline CapabilityLevel canCompile(Node* node)
     case PutByValDirect:
     case PutByValWithThis:
     case MatchStructure:
+    case FilterCallLinkStatus:
+    case FilterGetByIdStatus:
+    case FilterPutByIdStatus:
+    case FilterInByIdStatus:
+    case CreateThis:
         // These are OK.
         break;
 
@@ -365,7 +370,6 @@ inline CapabilityLevel canCompile(Node* node)
         break;
 
     case IdentityWithProfile:
-    case CreateThis:
     case CheckTierUpInLoop:
     case CheckTierUpAndOSREnter:
     case CheckTierUpAtReturn:
index f4e4048..3ba87e1 100644 (file)
@@ -856,6 +856,9 @@ private:
         case NewArrayWithSpread:
             compileNewArrayWithSpread();
             break;
+        case CreateThis:
+            compileCreateThis();
+            break;
         case Spread:
             compileSpread();
             break;
@@ -1298,6 +1301,12 @@ private:
         case CallDOMGetter:
             compileCallDOMGetter();
             break;
+        case FilterCallLinkStatus:
+        case FilterGetByIdStatus:
+        case FilterPutByIdStatus:
+        case FilterInByIdStatus:
+            compileFilterICStatus();
+            break;
 
         case PhantomLocal:
         case LoopHint:
@@ -5698,6 +5707,40 @@ private:
 
         setJSValue(result);
     }
+    
+    void compileCreateThis()
+    {
+        LValue callee = lowCell(m_node->child1());
+
+        LBasicBlock isFunctionBlock = m_out.newBlock();
+        LBasicBlock hasRareData = m_out.newBlock();
+        LBasicBlock slowPath = m_out.newBlock();
+        LBasicBlock continuation = m_out.newBlock();
+
+        m_out.branch(isFunction(callee, provenType(m_node->child1())), usually(isFunctionBlock), rarely(slowPath));
+
+        LBasicBlock lastNext = m_out.appendTo(isFunctionBlock, hasRareData);
+        LValue rareData = m_out.loadPtr(callee, m_heaps.JSFunction_rareData);
+        m_out.branch(m_out.isZero64(rareData), rarely(slowPath), usually(hasRareData));
+
+        m_out.appendTo(hasRareData, slowPath);
+        LValue allocator = m_out.loadPtr(rareData, m_heaps.FunctionRareData_allocator);
+        LValue structure = m_out.loadPtr(rareData, m_heaps.FunctionRareData_structure);
+        LValue butterfly = m_out.constIntPtr(0);
+        ValueFromBlock fastResult = m_out.anchor(allocateObject(allocator, structure, butterfly, slowPath));
+        m_out.jump(continuation);
+
+        m_out.appendTo(slowPath, continuation);
+        ValueFromBlock slowResult = m_out.anchor(vmCall(
+            Int64, m_out.operation(operationCreateThis), m_callFrame, callee, m_out.constInt32(m_node->inlineCapacity())));
+        m_out.jump(continuation);
+
+        m_out.appendTo(continuation, lastNext);
+        LValue result = m_out.phi(Int64, fastResult, slowResult);
+
+        mutatorFence();
+        setJSValue(result);
+    }
 
     void compileSpread()
     {
@@ -12191,6 +12234,11 @@ private:
         setJSValue(patchpoint);
     }
     
+    void compileFilterICStatus()
+    {
+        m_interpreter.filterICStatus(m_node);
+    }
+    
     void emitSwitchForMultiByOffset(LValue base, bool structuresChecked, Vector<SwitchCase, 2>& cases, LBasicBlock exit)
     {
         if (cases.isEmpty()) {
index 58f6283..f037c73 100644 (file)
@@ -96,10 +96,22 @@ CallVariantList PolymorphicCallStubRoutine::variants() const
     return result;
 }
 
+bool PolymorphicCallStubRoutine::hasEdges() const
+{
+    // The FTL does not count edges in its poly call stub routines. If the FTL went poly call, then
+    // it's not meaningful to keep profiling - we can just leave it at that. Remember, the FTL would
+    // have had full edge profiling from the DFG, and based on this information, it would have
+    // decided to go poly.
+    //
+    // There probably are very-difficult-to-imagine corner cases where the FTL not doing edge
+    // profiling is bad for polyvariant inlining. But polyvariant inlining is profitable sometimes
+    // while not having to increment counts is profitable always. So, we let the FTL run faster and
+    // not keep counts.
+    return !!m_fastCounts;
+}
+
 CallEdgeList PolymorphicCallStubRoutine::edges() const
 {
-    // We wouldn't have these if this was an FTL stub routine. We shouldn't be asking for profiling
-    // from the FTL.
     RELEASE_ASSERT(m_fastCounts);
     
     CallEdgeList result;
index 5647412..8097a2f 100644 (file)
@@ -90,6 +90,7 @@ public:
     virtual ~PolymorphicCallStubRoutine();
     
     CallVariantList variants() const;
+    bool hasEdges() const;
     CallEdgeList edges() const;
 
     void clearCallNodesFor(CallLinkInfo*);
index b00ca20..c08e419 100644 (file)
@@ -932,6 +932,7 @@ void linkVirtualFor(ExecState* exec, CallLinkInfo& callLinkInfo)
     MacroAssemblerCodeRef<JITStubRoutinePtrTag> virtualThunk = virtualThunkFor(&vm, callLinkInfo);
     revertCall(&vm, callLinkInfo, virtualThunk);
     callLinkInfo.setSlowStub(createJITStubRoutine(virtualThunk, vm, nullptr, true));
+    callLinkInfo.setClearedByVirtual();
 }
 
 namespace {
index 23e238c..6e93ce8 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012-2017 Apple Inc. All rights reserved.
+ * Copyright (C) 2012-2018 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -49,12 +49,12 @@ BytecodeSequence::BytecodeSequence(CodeBlock* codeBlock)
         m_header.append(out.toCString());
     }
     
-    StubInfoMap stubInfos;
-    codeBlock->getStubInfoMap(stubInfos);
+    ICStatusMap statusMap;
+    codeBlock->getICStatusMap(statusMap);
     
     for (unsigned bytecodeIndex = 0; bytecodeIndex < codeBlock->instructions().size();) {
         out.reset();
-        codeBlock->dumpBytecode(out, bytecodeIndex, stubInfos);
+        codeBlock->dumpBytecode(out, bytecodeIndex, statusMap);
         OpcodeID opcodeID = Interpreter::getOpcodeID(codeBlock->instructions()[bytecodeIndex].u.opcode);
         m_sequence.append(Bytecode(bytecodeIndex, opcodeID, out.toCString()));
         bytecodeIndex += opcodeLength(opcodeID);
index fca3b0a..8802f7c 100644 (file)
@@ -275,6 +275,7 @@ constexpr bool enableWebAssemblyStreamingApi = false;
     v(unsigned, maxAccessVariantListSize, 8, Normal, nullptr) \
     v(bool, usePolyvariantDevirtualization, true, Normal, nullptr) \
     v(bool, usePolymorphicAccessInlining, true, Normal, nullptr) \
+    v(unsigned, maxPolymorphicAccessInliningListSize, 8, Normal, nullptr) \
     v(bool, usePolymorphicCallInlining, true, Normal, nullptr) \
     v(bool, usePolymorphicCallInliningForNonStubStatus, false, Normal, nullptr) \
     v(unsigned, maxPolymorphicCallVariantListSize, 15, Normal, nullptr) \
@@ -322,9 +323,6 @@ constexpr bool enableWebAssemblyStreamingApi = false;
     \
     v(unsigned, maximumVarargsForInlining, 100, Normal, nullptr) \
     \
-    v(bool, usePolyvariantCallInlining, true, Normal, nullptr) \
-    v(bool, usePolyvariantByIdInlining, true, Normal, nullptr) \
-    \
     v(bool, useMaximalFlushInsertionPhase, false, Normal, "Setting to true allows the DFG's MaximalFlushInsertionPhase to run.") \
     \
     v(unsigned, maximumBinaryStringSwitchCaseLength, 50, Normal, nullptr) \
index 6084c84..ccd6d53 100644 (file)
@@ -1,3 +1,13 @@
+2018-06-06  Filip Pizlo  <fpizlo@apple.com>
+
+        We should support CreateThis in the FTL
+        https://bugs.webkit.org/show_bug.cgi?id=164904
+
+        Reviewed by Yusuke Suzuki.
+
+        * wtf/TinyPtrSet.h:
+        (WTF::TinyPtrSet::operator!= const):
+
 2018-07-21  Yusuke Suzuki  <utatane.tea@gmail.com>
 
         [JSC] Use Function / ScopedLambda / RecursableLambda instead of std::function
index 4668bd5..3f63ce3 100644 (file)
@@ -351,6 +351,11 @@ public:
         return isSubsetOf(other);
     }
     
+    bool operator!=(const TinyPtrSet& other) const
+    {
+        return !(*this == other);
+    }
+    
 private:
     friend class JSC::DFG::StructureAbstractValue;