Diff
Modified: trunk/Source/_javascript_Core/ChangeLog (221601 => 221602)
--- trunk/Source/_javascript_Core/ChangeLog 2017-09-05 03:03:03 UTC (rev 221601)
+++ trunk/Source/_javascript_Core/ChangeLog 2017-09-05 03:21:33 UTC (rev 221602)
@@ -1,3 +1,140 @@
+2017-09-04 Saam Barati <[email protected]>
+
+ Support compiling catch in the FTL
+ https://bugs.webkit.org/show_bug.cgi?id=175396
+
+ Reviewed by Filip Pizlo.
+
+ This patch implements op_catch in the FTL. It extends the DFG implementation
+ by supporting multiple entrypoints in DFG-SSA. This patch implements this
+ by introducing an EntrySwitch node. When converting to SSA, we introduce a new
+ root block with an EntrySwitch that has the previous DFG entrypoints as its
+ successors. By convention, we pick the zeroth entry point index to be the
+ op_enter entrypoint. Like in B3, in DFG-SSA, EntrySwitch just acts like a
+ switch over the entrypoint index argument. DFG::EntrySwitch in the FTL
+ simply lowers to B3::EntrySwitch. The EntrySwitch in the root block that
+ SSAConversion creates can not exit because we would both not know where to exit
+ to in the program: we would not have valid OSR exit state. This design also
+ mandates that anything we hoist above EntrySwitch in the new root block
+ can not exit since they also do not have valid OSR exit state.
+
+ This patch also adds a new metadata node named InitializeEntrypointArguments.
+ InitializeEntrypointArguments is a metadata node that initializes the flush format for
+ the arguments at a given entrypoint. For a given entrypoint index, this node
+ tells AI and OSRAvailabilityAnalysis what the flush format for each argument
+ is. This allows each individual entrypoint to have an independent set of
+ argument types. Currently, this won't happen in practice because ArgumentPosition
+ unifies flush formats, but this is an implementation detail we probably want
+ to modify in the future. SSAConversion will add InitializeEntrypointArguments
+ to the beginning of each of the original DFG entrypoint blocks.
+
+ This patch also adds the ability to specify custom prologue code generators in Air.
+ This allows the FTL to specify a custom prologue for catch entrypoints that
+ matches the op_catch OSR entry calling convention that the DFG uses. This way,
+ the baseline JIT code OSR enters into op_catch the same way both in the DFG
+ and the FTL. In the future, we can use this same mechanism to perform stack
+ overflow checks instead of using a patchpoint.
+
+ * b3/air/AirCode.cpp:
+ (JSC::B3::Air::Code::isEntrypoint):
+ (JSC::B3::Air::Code::entrypointIndex):
+ * b3/air/AirCode.h:
+ (JSC::B3::Air::Code::setPrologueForEntrypoint):
+ (JSC::B3::Air::Code::prologueGeneratorForEntrypoint):
+ * b3/air/AirGenerate.cpp:
+ (JSC::B3::Air::generate):
+ * dfg/DFGAbstractInterpreterInlines.h:
+ (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+ * dfg/DFGBasicBlock.h:
+ * dfg/DFGByteCodeParser.cpp:
+ (JSC::DFG::ByteCodeParser::parseBlock):
+ (JSC::DFG::ByteCodeParser::parse):
+ * dfg/DFGCFG.h:
+ (JSC::DFG::selectCFG):
+ * dfg/DFGClobberize.h:
+ (JSC::DFG::clobberize):
+ * dfg/DFGClobbersExitState.cpp:
+ (JSC::DFG::clobbersExitState):
+ * dfg/DFGCommonData.cpp:
+ (JSC::DFG::CommonData::shrinkToFit):
+ (JSC::DFG::CommonData::finalizeCatchEntrypoints):
+ * dfg/DFGCommonData.h:
+ (JSC::DFG::CommonData::catchOSREntryDataForBytecodeIndex):
+ (JSC::DFG::CommonData::appendCatchEntrypoint):
+ * dfg/DFGDoesGC.cpp:
+ (JSC::DFG::doesGC):
+ * dfg/DFGFixupPhase.cpp:
+ (JSC::DFG::FixupPhase::fixupNode):
+ * dfg/DFGGraph.cpp:
+ (JSC::DFG::Graph::dump):
+ (JSC::DFG::Graph::invalidateCFG):
+ (JSC::DFG::Graph::ensureCPSCFG):
+ (JSC::DFG::Graph::methodOfGettingAValueProfileFor):
+ * dfg/DFGGraph.h:
+ (JSC::DFG::Graph::isEntrypoint):
+ * dfg/DFGInPlaceAbstractState.cpp:
+ (JSC::DFG::InPlaceAbstractState::initialize):
+ (JSC::DFG::InPlaceAbstractState::mergeToSuccessors):
+ * dfg/DFGJITCode.cpp:
+ (JSC::DFG::JITCode::shrinkToFit):
+ (JSC::DFG::JITCode::finalizeOSREntrypoints):
+ * dfg/DFGJITCode.h:
+ (JSC::DFG::JITCode::catchOSREntryDataForBytecodeIndex): Deleted.
+ (JSC::DFG::JITCode::appendCatchEntrypoint): Deleted.
+ * dfg/DFGJITCompiler.cpp:
+ (JSC::DFG::JITCompiler::noticeCatchEntrypoint):
+ (JSC::DFG::JITCompiler::makeCatchOSREntryBuffer):
+ * dfg/DFGMayExit.cpp:
+ * dfg/DFGNode.h:
+ (JSC::DFG::Node::isEntrySwitch):
+ (JSC::DFG::Node::isTerminal):
+ (JSC::DFG::Node::entrySwitchData):
+ (JSC::DFG::Node::numSuccessors):
+ (JSC::DFG::Node::successor):
+ (JSC::DFG::Node::entrypointIndex):
+ * dfg/DFGNodeType.h:
+ * dfg/DFGOSRAvailabilityAnalysisPhase.cpp:
+ (JSC::DFG::OSRAvailabilityAnalysisPhase::run):
+ (JSC::DFG::LocalOSRAvailabilityCalculator::executeNode):
+ * dfg/DFGOSREntry.cpp:
+ (JSC::DFG::prepareCatchOSREntry):
+ * dfg/DFGOSREntry.h:
+ * dfg/DFGOSREntrypointCreationPhase.cpp:
+ (JSC::DFG::OSREntrypointCreationPhase::run):
+ * dfg/DFGPredictionPropagationPhase.cpp:
+ * dfg/DFGSSAConversionPhase.cpp:
+ (JSC::DFG::SSAConversionPhase::SSAConversionPhase):
+ (JSC::DFG::SSAConversionPhase::run):
+ * dfg/DFGSafeToExecute.h:
+ (JSC::DFG::safeToExecute):
+ * dfg/DFGSpeculativeJIT.cpp:
+ (JSC::DFG::SpeculativeJIT::linkOSREntries):
+ * dfg/DFGSpeculativeJIT32_64.cpp:
+ (JSC::DFG::SpeculativeJIT::compile):
+ * dfg/DFGSpeculativeJIT64.cpp:
+ (JSC::DFG::SpeculativeJIT::compile):
+ * dfg/DFGStaticExecutionCountEstimationPhase.cpp:
+ (JSC::DFG::StaticExecutionCountEstimationPhase::run):
+ * dfg/DFGValidate.cpp:
+ * ftl/FTLCapabilities.cpp:
+ (JSC::FTL::canCompile):
+ * ftl/FTLCompile.cpp:
+ (JSC::FTL::compile):
+ * ftl/FTLLowerDFGToB3.cpp:
+ (JSC::FTL::DFG::LowerDFGToB3::lower):
+ (JSC::FTL::DFG::LowerDFGToB3::compileNode):
+ (JSC::FTL::DFG::LowerDFGToB3::compileExtractCatchLocal):
+ (JSC::FTL::DFG::LowerDFGToB3::compileGetStack):
+ (JSC::FTL::DFG::LowerDFGToB3::compileEntrySwitch):
+ (JSC::FTL::DFG::LowerDFGToB3::speculate):
+ (JSC::FTL::DFG::LowerDFGToB3::appendOSRExitDescriptor):
+ (JSC::FTL::DFG::LowerDFGToB3::appendOSRExit):
+ (JSC::FTL::DFG::LowerDFGToB3::blessSpeculation):
+ * ftl/FTLOutput.cpp:
+ (JSC::FTL::Output::entrySwitch):
+ * ftl/FTLOutput.h:
+ * jit/JITOperations.cpp:
+
2017-09-03 Yusuke Suzuki <[email protected]>
[DFG][FTL] Efficiently execute number#toString()
Modified: trunk/Source/_javascript_Core/b3/air/AirCode.cpp (221601 => 221602)
--- trunk/Source/_javascript_Core/b3/air/AirCode.cpp 2017-09-05 03:03:03 UTC (rev 221601)
+++ trunk/Source/_javascript_Core/b3/air/AirCode.cpp 2017-09-05 03:21:33 UTC (rev 221602)
@@ -156,6 +156,8 @@
bool Code::isEntrypoint(BasicBlock* block) const
{
+ // Note: This function must work both before and after LowerEntrySwitch.
+
if (m_entrypoints.isEmpty())
return !block->index();
@@ -166,6 +168,16 @@
return false;
}
+std::optional<unsigned> Code::entrypointIndex(BasicBlock* block) const
+{
+ RELEASE_ASSERT(m_entrypoints.size());
+ for (unsigned i = 0; i < m_entrypoints.size(); ++i) {
+ if (m_entrypoints[i].block() == block)
+ return i;
+ }
+ return std::nullopt;
+}
+
void Code::setCalleeSaveRegisterAtOffsetList(RegisterAtOffsetList&& registerAtOffsetList, StackSlot* slot)
{
m_uncorrectedCalleeSaveRegisterAtOffsetList = WTFMove(registerAtOffsetList);
Modified: trunk/Source/_javascript_Core/b3/air/AirCode.h (221601 => 221602)
--- trunk/Source/_javascript_Core/b3/air/AirCode.h 2017-09-05 03:03:03 UTC (rev 221601)
+++ trunk/Source/_javascript_Core/b3/air/AirCode.h 2017-09-05 03:21:33 UTC (rev 221602)
@@ -53,11 +53,15 @@
class BlockInsertionSet;
class CCallSpecial;
class CFG;
+class Code;
class Disassembler;
typedef void WasmBoundsCheckGeneratorFunction(CCallHelpers&, GPRReg);
typedef SharedTask<WasmBoundsCheckGeneratorFunction> WasmBoundsCheckGenerator;
+typedef void PrologueGeneratorFunction(CCallHelpers&, Code&);
+typedef SharedTask<PrologueGeneratorFunction> PrologueGenerator;
+
// This is an IR that is very close to the bare metal. It requires about 40x more bytes than the
// generated machine code - for example if you're generating 1MB of machine code, you need about
// 40MB of Air.
@@ -165,7 +169,18 @@
const Vector<FrequentedBlock>& entrypoints() const { return m_entrypoints; }
const FrequentedBlock& entrypoint(unsigned index) const { return m_entrypoints[index]; }
bool isEntrypoint(BasicBlock*) const;
-
+ // Note: It is only valid to call this function after LowerEntrySwitch.
+ std::optional<unsigned> entrypointIndex(BasicBlock*) const;
+ void setPrologueForEntrypoint(unsigned entrypointIndex, RefPtr<PrologueGenerator> generator)
+ {
+ // Note: We allow this to be called even before we set m_entrypoints just for convenience to users of this API.
+ m_entrypointIndexToGenerator.set(entrypointIndex, generator);
+ }
+ RefPtr<PrologueGenerator> prologueGeneratorForEntrypoint(unsigned entrypointIndex)
+ {
+ return m_entrypointIndexToGenerator.get(entrypointIndex);
+ }
+
// This is used by lowerEntrySwitch().
template<typename Vector>
void setEntrypoints(Vector&& vector)
@@ -353,6 +368,7 @@
StackSlot* m_calleeSaveStackSlot { nullptr };
Vector<FrequentedBlock> m_entrypoints; // This is empty until after lowerEntrySwitch().
Vector<CCallHelpers::Label> m_entrypointLabels; // This is empty until code generation.
+ HashMap<unsigned, RefPtr<PrologueGenerator>, WTF::IntHash<unsigned>, WTF::UnsignedWithZeroKeyHashTraits<unsigned>> m_entrypointIndexToGenerator;
RefPtr<WasmBoundsCheckGenerator> m_wasmBoundsCheckGenerator;
const char* m_lastPhaseName;
std::unique_ptr<Disassembler> m_disassembler;
Modified: trunk/Source/_javascript_Core/b3/air/AirGenerate.cpp (221601 => 221602)
--- trunk/Source/_javascript_Core/b3/air/AirGenerate.cpp 2017-09-05 03:03:03 UTC (rev 221601)
+++ trunk/Source/_javascript_Core/b3/air/AirGenerate.cpp 2017-09-05 03:21:33 UTC (rev 221602)
@@ -208,21 +208,28 @@
if (disassembler)
disassembler->startBlock(block, jit);
- if (code.isEntrypoint(block)) {
+ if (std::optional<unsigned> entrypointIndex = code.entrypointIndex(block)) {
+ ASSERT(code.isEntrypoint(block));
+
if (disassembler)
disassembler->startEntrypoint(jit);
- jit.emitFunctionPrologue();
- if (code.frameSize()) {
- AllowMacroScratchRegisterUsageIf allowScratch(jit, isARM64());
- jit.addPtr(CCallHelpers::TrustedImm32(-code.frameSize()), MacroAssembler::stackPointerRegister);
+ if (RefPtr<PrologueGenerator> prologueGenerator = code.prologueGeneratorForEntrypoint(*entrypointIndex))
+ prologueGenerator->run(jit, code);
+ else {
+ jit.emitFunctionPrologue();
+ if (code.frameSize()) {
+ AllowMacroScratchRegisterUsageIf allowScratch(jit, isARM64());
+ jit.addPtr(CCallHelpers::TrustedImm32(-code.frameSize()), MacroAssembler::stackPointerRegister);
+ }
+
+ jit.emitSave(code.calleeSaveRegisterAtOffsetList());
}
-
- jit.emitSave(code.calleeSaveRegisterAtOffsetList());
if (disassembler)
disassembler->endEntrypoint(jit);
- }
+ } else
+ ASSERT(!code.isEntrypoint(block));
ASSERT(block->size() >= 1);
for (unsigned i = 0; i < block->size() - 1; ++i) {
Modified: trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h (221601 => 221602)
--- trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h 2017-09-05 03:03:03 UTC (rev 221601)
+++ trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h 2017-09-05 03:21:33 UTC (rev 221602)
@@ -260,7 +260,7 @@
// DFG execution.
break;
}
-
+
case KillStack: {
// This is just a hint telling us that the OSR state of the local is no longer inside the
// flushed data.
@@ -273,7 +273,33 @@
// non-clear value.
ASSERT(!m_state.variables().operand(node->local()).isClear());
break;
-
+
+ case InitializeEntrypointArguments: {
+ unsigned entrypointIndex = node->entrypointIndex();
+ const Vector<FlushFormat>& argumentFormats = m_graph.m_argumentFormats[entrypointIndex];
+ for (unsigned argument = 0; argument < argumentFormats.size(); ++argument) {
+ AbstractValue& value = m_state.variables().argument(argument);
+ switch (argumentFormats[argument]) {
+ case FlushedInt32:
+ value.setType(SpecInt32Only);
+ break;
+ case FlushedBoolean:
+ value.setType(SpecBoolean);
+ break;
+ case FlushedCell:
+ value.setType(m_graph, SpecCell);
+ break;
+ case FlushedJSValue:
+ value.makeBytecodeTop();
+ break;
+ default:
+ DFG_CRASH(m_graph, node, "Bad flush format for argument");
+ break;
+ }
+ }
+ break;
+ }
+
case LoadVarargs:
case ForwardVarargs: {
// FIXME: ForwardVarargs should check if the count becomes known, and if it does, it should turn
@@ -1866,6 +1892,9 @@
break;
}
+ case EntrySwitch:
+ break;
+
case Return:
m_state.setIsValid(false);
break;
Modified: trunk/Source/_javascript_Core/dfg/DFGBasicBlock.h (221601 => 221602)
--- trunk/Source/_javascript_Core/dfg/DFGBasicBlock.h 2017-09-05 03:03:03 UTC (rev 221601)
+++ trunk/Source/_javascript_Core/dfg/DFGBasicBlock.h 2017-09-05 03:21:33 UTC (rev 221602)
@@ -221,8 +221,6 @@
float executionCount;
- // These fields are reserved for NaturalLoops.
-
struct SSAData {
WTF_MAKE_FAST_ALLOCATED;
public:
Modified: trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp (221601 => 221602)
--- trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp 2017-09-05 03:03:03 UTC (rev 221601)
+++ trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp 2017-09-05 03:21:33 UTC (rev 221602)
@@ -5262,9 +5262,7 @@
NEXT_OPCODE(op_catch);
}
- if (isFTL(m_graph.m_plan.mode)) {
- // FIXME: Support catch in the FTL.
- // https://bugs.webkit.org/show_bug.cgi?id=175396
+ if (m_graph.m_plan.mode == FTLForOSREntryMode) {
NEXT_OPCODE(op_catch);
}
@@ -6465,8 +6463,6 @@
m_graph.determineReachability();
m_graph.killUnreachableBlocks();
- m_graph.m_cpsCFG = std::make_unique<CPSCFG>(m_graph);
-
for (BlockIndex blockIndex = m_graph.numBlocks(); blockIndex--;) {
BasicBlock* block = m_graph.block(blockIndex);
if (!block)
Modified: trunk/Source/_javascript_Core/dfg/DFGCFG.h (221601 => 221602)
--- trunk/Source/_javascript_Core/dfg/DFGCFG.h 2017-09-05 03:03:03 UTC (rev 221601)
+++ trunk/Source/_javascript_Core/dfg/DFGCFG.h 2017-09-05 03:21:33 UTC (rev 221602)
@@ -98,8 +98,7 @@
template <typename T, typename = typename std::enable_if<std::is_same<T, CPSCFG>::value>::type>
CPSCFG& selectCFG(Graph& graph)
{
- RELEASE_ASSERT(graph.m_cpsCFG);
- return *graph.m_cpsCFG;
+ return graph.ensureCPSCFG();
}
template <typename T, typename = typename std::enable_if<std::is_same<T, SSACFG>::value>::type>
Modified: trunk/Source/_javascript_Core/dfg/DFGClobberize.h (221601 => 221602)
--- trunk/Source/_javascript_Core/dfg/DFGClobberize.h 2017-09-05 03:03:03 UTC (rev 221601)
+++ trunk/Source/_javascript_Core/dfg/DFGClobberize.h 2017-09-05 03:21:33 UTC (rev 221602)
@@ -426,6 +426,7 @@
case Jump:
case Branch:
case Switch:
+ case EntrySwitch:
case ForceOSRExit:
case CheckBadCell:
case Return:
@@ -437,6 +438,7 @@
case ProfileType:
case ProfileControlFlow:
case PutHint:
+ case InitializeEntrypointArguments:
write(SideState);
return;
Modified: trunk/Source/_javascript_Core/dfg/DFGClobbersExitState.cpp (221601 => 221602)
--- trunk/Source/_javascript_Core/dfg/DFGClobbersExitState.cpp 2017-09-05 03:03:03 UTC (rev 221601)
+++ trunk/Source/_javascript_Core/dfg/DFGClobbersExitState.cpp 2017-09-05 03:21:33 UTC (rev 221602)
@@ -40,6 +40,7 @@
// There are certain nodes whose effect on the exit state has nothing to do with what they
// normally clobber.
switch (node->op()) {
+ case InitializeEntrypointArguments:
case MovHint:
case ZombieHint:
case PutHint:
Modified: trunk/Source/_javascript_Core/dfg/DFGCommonData.cpp (221601 => 221602)
--- trunk/Source/_javascript_Core/dfg/DFGCommonData.cpp 2017-09-05 03:03:03 UTC (rev 221601)
+++ trunk/Source/_javascript_Core/dfg/DFGCommonData.cpp 2017-09-05 03:21:33 UTC (rev 221602)
@@ -87,6 +87,7 @@
codeOrigins.shrinkToFit();
weakReferences.shrinkToFit();
transitions.shrinkToFit();
+ catchEntrypoints.shrinkToFit();
}
static StaticLock pcCodeBlockMapLock;
@@ -193,6 +194,17 @@
watchpoint->key().validateReferences(trackedReferences);
}
+void CommonData::finalizeCatchEntrypoints()
+{
+ std::sort(catchEntrypoints.begin(), catchEntrypoints.end(),
+ [] (const CatchEntrypointData& a, const CatchEntrypointData& b) { return a.bytecodeIndex < b.bytecodeIndex; });
+
+#if !ASSERT_DISABLED
+ for (unsigned i = 0; i + 1 < catchEntrypoints.size(); ++i)
+ ASSERT(catchEntrypoints[i].bytecodeIndex <= catchEntrypoints[i + 1].bytecodeIndex);
+#endif
+}
+
} } // namespace JSC::DFG
#endif // ENABLE(DFG_JIT)
Modified: trunk/Source/_javascript_Core/dfg/DFGCommonData.h (221601 => 221602)
--- trunk/Source/_javascript_Core/dfg/DFGCommonData.h 2017-09-05 03:03:03 UTC (rev 221601)
+++ trunk/Source/_javascript_Core/dfg/DFGCommonData.h 2017-09-05 03:21:33 UTC (rev 221602)
@@ -31,6 +31,7 @@
#include "DFGAdaptiveInferredPropertyValueWatchpoint.h"
#include "DFGAdaptiveStructureWatchpoint.h"
#include "DFGJumpReplacement.h"
+#include "DFGOSREntry.h"
#include "InlineCallFrameSet.h"
#include "JSCell.h"
#include "ProfilerCompilation.h"
@@ -90,6 +91,20 @@
void installVMTrapBreakpoints(CodeBlock* owner);
bool isVMTrapBreakpoint(void* address);
+ CatchEntrypointData* catchOSREntryDataForBytecodeIndex(unsigned bytecodeIndex)
+ {
+ return tryBinarySearch<CatchEntrypointData, unsigned>(
+ catchEntrypoints, catchEntrypoints.size(), bytecodeIndex,
+ [] (const CatchEntrypointData* item) { return item->bytecodeIndex; });
+ }
+
+ void appendCatchEntrypoint(unsigned bytecodeIndex, void* machineCode, Vector<FlushFormat>&& argumentFormats)
+ {
+ catchEntrypoints.append(CatchEntrypointData { machineCode, WTFMove(argumentFormats), bytecodeIndex });
+ }
+
+ void finalizeCatchEntrypoints();
+
unsigned requiredRegisterCountForExecutionAndExit() const
{
return std::max(frameRegisterCount, requiredRegisterCountForExit);
@@ -106,11 +121,13 @@
Vector<WeakReferenceTransition> transitions;
Vector<WriteBarrier<JSCell>> weakReferences;
Vector<WriteBarrier<Structure>> weakStructureReferences;
+ Vector<CatchEntrypointData> catchEntrypoints;
Bag<CodeBlockJettisoningWatchpoint> watchpoints;
Bag<AdaptiveStructureWatchpoint> adaptiveStructureWatchpoints;
Bag<AdaptiveInferredPropertyValueWatchpoint> adaptiveInferredPropertyValueWatchpoints;
Vector<JumpReplacement> jumpReplacements;
+ ScratchBuffer* catchOSREntryBuffer;
RefPtr<Profiler::Compilation> compilation;
bool livenessHasBeenProved; // Initialized and used on every GC.
bool allTransitionsHaveBeenMarked; // Initialized and used on every GC.
Modified: trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp (221601 => 221602)
--- trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp 2017-09-05 03:03:03 UTC (rev 221601)
+++ trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp 2017-09-05 03:21:33 UTC (rev 221602)
@@ -56,6 +56,7 @@
case GetLocal:
case SetLocal:
case MovHint:
+ case InitializeEntrypointArguments:
case ZombieHint:
case ExitOK:
case Phantom:
@@ -184,6 +185,7 @@
case Jump:
case Branch:
case Switch:
+ case EntrySwitch:
case Return:
case TailCall:
case DirectTailCall:
Modified: trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp (221601 => 221602)
--- trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp 2017-09-05 03:03:03 UTC (rev 221601)
+++ trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp 2017-09-05 03:21:33 UTC (rev 221602)
@@ -1549,6 +1549,7 @@
case Phi:
case Upsilon:
+ case EntrySwitch:
case GetIndexedPropertyStorage:
case LastNodeType:
case CheckTierUpInLoop:
@@ -2037,6 +2038,7 @@
case ExtractCatchLocal:
case LoopHint:
case MovHint:
+ case InitializeEntrypointArguments:
case ZombieHint:
case ExitOK:
case BottomValue:
Modified: trunk/Source/_javascript_Core/dfg/DFGGraph.cpp (221601 => 221602)
--- trunk/Source/_javascript_Core/dfg/DFGGraph.cpp 2017-09-05 03:03:03 UTC (rev 221601)
+++ trunk/Source/_javascript_Core/dfg/DFGGraph.cpp 2017-09-05 03:21:33 UTC (rev 221602)
@@ -368,6 +368,11 @@
out.print(comma, inContext(data->cases[i].value, context), ":", data->cases[i].target);
out.print(comma, "default:", data->fallThrough);
}
+ if (node->isEntrySwitch()) {
+ EntrySwitchData* data = ""
+ for (unsigned i = 0; i < data->cases.size(); ++i)
+ out.print(comma, BranchTarget(data->cases[i]));
+ }
ClobberSet reads;
ClobberSet writes;
addReadsAndWrites(*this, node, reads, writes);
@@ -515,8 +520,10 @@
out.print("\n");
out.print("DFG for ", CodeBlockWithJITType(m_codeBlock, JITCode::DFGJIT), ":\n");
out.print(" Fixpoint state: ", m_fixpointState, "; Form: ", m_form, "; Unification state: ", m_unificationState, "; Ref count state: ", m_refCountState, "\n");
- if (m_form == SSA)
- out.print(" Argument formats: ", listDump(m_argumentFormats), "\n");
+ if (m_form == SSA) {
+ for (unsigned entrypointIndex = 0; entrypointIndex < m_argumentFormats.size(); ++entrypointIndex)
+ out.print(" Argument formats for entrypoint index: ", entrypointIndex, " : ", listDump(m_argumentFormats[entrypointIndex]), "\n");
+ }
else {
for (auto pair : m_entrypointToArguments)
out.print(" Arguments for block#", pair.key->index, ": ", listDump(pair.value), "\n");
@@ -824,6 +831,7 @@
m_controlEquivalenceAnalysis = nullptr;
m_backwardsDominators = nullptr;
m_backwardsCFG = nullptr;
+ m_cpsCFG = nullptr;
}
void Graph::invalidateNodeLiveness()
@@ -1543,6 +1551,14 @@
logDFGAssertionFailure(*this, toCString("While handling block ", pointerDump(block), "\n\n"), file, line, function, assertion);
}
+CPSCFG& Graph::ensureCPSCFG()
+{
+ RELEASE_ASSERT(m_form != SSA && !m_isInSSAConversion);
+ if (!m_cpsCFG)
+ m_cpsCFG = std::make_unique<CPSCFG>(*this);
+ return *m_cpsCFG;
+}
+
CPSDominators& Graph::ensureCPSDominators()
{
RELEASE_ASSERT(m_form != SSA && !m_isInSSAConversion);
@@ -1610,21 +1626,14 @@
CodeBlock* profiledBlock = baselineCodeBlockFor(node->origin.semantic);
if (node->accessesStack(*this)) {
- ValueProfile* result = [&] () -> ValueProfile* {
- if (!node->local().isArgument())
- return nullptr;
+ if (m_form != SSA && node->local().isArgument()) {
int argument = node->local().toArgument();
+ Node* argumentNode = m_entrypointToArguments.find(block(0))->value[argument];
// FIXME: We should match SetArgument nodes at other entrypoints as well:
// https://bugs.webkit.org/show_bug.cgi?id=175841
- Node* argumentNode = m_entrypointToArguments.find(block(0))->value[argument];
- if (!argumentNode)
- return nullptr;
- if (node->variableAccessData() != argumentNode->variableAccessData())
- return nullptr;
- return &profiledBlock->valueProfileForArgument(argument);
- }();
- if (result)
- return result;
+ if (argumentNode && node->variableAccessData() == argumentNode->variableAccessData())
+ return &profiledBlock->valueProfileForArgument(argument);
+ }
if (node->op() == GetLocal) {
return MethodOfGettingAValueProfile::fromLazyOperand(
Modified: trunk/Source/_javascript_Core/dfg/DFGGraph.h (221601 => 221602)
--- trunk/Source/_javascript_Core/dfg/DFGGraph.h 2017-09-05 03:03:03 UTC (rev 221601)
+++ trunk/Source/_javascript_Core/dfg/DFGGraph.h 2017-09-05 03:21:33 UTC (rev 221602)
@@ -931,6 +931,7 @@
BackwardsCFG& ensureBackwardsCFG();
BackwardsDominators& ensureBackwardsDominators();
ControlEquivalenceAnalysis& ensureControlEquivalenceAnalysis();
+ CPSCFG& ensureCPSCFG();
// These functions only makes sense to call after bytecode parsing
// because it queries the m_hasExceptionHandlers boolean whose value
@@ -950,6 +951,14 @@
bool isEntrypoint(BasicBlock* block) const
{
+ ASSERT_WITH_MESSAGE(!m_isInSSAConversion, "This is not written to work during SSA conversion.");
+
+ if (m_form == SSA) {
+ ASSERT(m_entrypoints.size() == 1);
+ ASSERT(m_entrypoints.contains(this->block(0)));
+ return block == this->block(0);
+ }
+
if (m_entrypoints.size() <= 4) {
bool result = m_entrypoints.contains(block);
ASSERT(result == m_entrypointToArguments.contains(block));
@@ -978,12 +987,13 @@
// In CPS, this is all of the SetArgument nodes for the arguments in the machine code block
// that survived DCE. All of them except maybe "this" will survive DCE, because of the Flush
- // nodes.
+ // nodes. In SSA, this has no meaning. It's empty.
+ HashMap<BasicBlock*, ArgumentsVector> m_entrypointToArguments;
+
+ // In SSA, this is the argument speculation that we've locked in for an entrypoint block.
//
- // In SSA, this is all of the GetStack nodes for the arguments in the machine code block that
- // may have some speculation in the prologue and survived DCE. Note that to get the speculation
- // for an argument in SSA, you must use m_argumentFormats, since we still have to speculate
- // even if the argument got killed. For example:
+ // We must speculate on the argument types at each entrypoint even if operations involving
+ // arguments get killed. For example:
//
// function foo(x) {
// var tmp = x + 1;
@@ -1001,11 +1011,21 @@
//
// If we DCE the ArithAdd and we remove the int check on x, then this won't do the side
// effects.
- HashMap<BasicBlock*, ArgumentsVector> m_entrypointToArguments;
-
- // In CPS, this is meaningless. In SSA, this is the argument speculation that we've locked in.
- Vector<FlushFormat> m_argumentFormats;
+ //
+ // By convention, entrypoint index 0 is used for the CodeBlock's op_enter entrypoint.
+ // So argumentFormats[0] are the argument formats for the normal call entrypoint.
+ Vector<Vector<FlushFormat>> m_argumentFormats;
+ // This maps an entrypoint index to a particular op_catch bytecode offset. By convention,
+ // it'll never have zero as a key because we use zero to mean the op_enter entrypoint.
+ HashMap<unsigned, unsigned> m_entrypointIndexToCatchBytecodeOffset;
+
+ // This is the number of logical entrypoints that we're compiling. This is only used
+ // in SSA. Each EntrySwitch node must have numberOfEntrypoints cases. Note, this is
+ // not the same as m_entrypoints.size(). m_entrypoints.size() represents the number
+ // of roots in the CFG. In SSA, m_entrypoints.size() == 1.
+ unsigned m_numberOfEntrypoints { UINT_MAX };
+
SegmentedVector<VariableAccessData, 16> m_variableAccessData;
SegmentedVector<ArgumentPosition, 8> m_argumentPositions;
Bag<Transition> m_transitions;
@@ -1060,6 +1080,7 @@
std::optional<uint32_t> m_maxLocalsForCatchOSREntry;
std::unique_ptr<FlowIndexing> m_indexingCache;
std::unique_ptr<FlowMap<AbstractValue>> m_abstractValuesCache;
+ Bag<EntrySwitchData> m_entrySwitchData;
RegisteredStructure stringStructure;
RegisteredStructure symbolStructure;
Modified: trunk/Source/_javascript_Core/dfg/DFGInPlaceAbstractState.cpp (221601 => 221602)
--- trunk/Source/_javascript_Core/dfg/DFGInPlaceAbstractState.cpp 2017-09-05 03:03:03 UTC (rev 221601)
+++ trunk/Source/_javascript_Core/dfg/DFGInPlaceAbstractState.cpp 2017-09-05 03:21:33 UTC (rev 221602)
@@ -100,16 +100,18 @@
entrypoint->cfaStructureClobberStateAtHead = StructuresAreWatched;
entrypoint->cfaStructureClobberStateAtTail = StructuresAreWatched;
- for (size_t i = 0; i < entrypoint->valuesAtHead.numberOfArguments(); ++i) {
- entrypoint->valuesAtTail.argument(i).clear();
+ if (m_graph.m_form == SSA) {
+ for (size_t i = 0; i < entrypoint->valuesAtHead.numberOfArguments(); ++i) {
+ entrypoint->valuesAtHead.argument(i).clear();
+ entrypoint->valuesAtTail.argument(i).clear();
+ }
+ } else {
+ const ArgumentsVector& arguments = m_graph.m_entrypointToArguments.find(entrypoint)->value;
+ for (size_t i = 0; i < entrypoint->valuesAtHead.numberOfArguments(); ++i) {
+ entrypoint->valuesAtTail.argument(i).clear();
- FlushFormat format;
- if (m_graph.m_form == SSA) {
- // FIXME: When supporting multiple entrypoints in the FTL, we need to change
- // what we do here: https://bugs.webkit.org/show_bug.cgi?id=175396
- format = m_graph.m_argumentFormats[i];
- } else {
- Node* node = m_graph.m_entrypointToArguments.find(entrypoint)->value[i];
+ FlushFormat format;
+ Node* node = arguments[i];
if (!node)
format = FlushedJSValue;
else {
@@ -116,26 +118,27 @@
ASSERT(node->op() == SetArgument);
format = node->variableAccessData()->flushFormat();
}
+
+ switch (format) {
+ case FlushedInt32:
+ entrypoint->valuesAtHead.argument(i).setType(SpecInt32Only);
+ break;
+ case FlushedBoolean:
+ entrypoint->valuesAtHead.argument(i).setType(SpecBoolean);
+ break;
+ case FlushedCell:
+ entrypoint->valuesAtHead.argument(i).setType(m_graph, SpecCell);
+ break;
+ case FlushedJSValue:
+ entrypoint->valuesAtHead.argument(i).makeBytecodeTop();
+ break;
+ default:
+ DFG_CRASH(m_graph, nullptr, "Bad flush format for argument");
+ break;
+ }
}
-
- switch (format) {
- case FlushedInt32:
- entrypoint->valuesAtHead.argument(i).setType(SpecInt32Only);
- break;
- case FlushedBoolean:
- entrypoint->valuesAtHead.argument(i).setType(SpecBoolean);
- break;
- case FlushedCell:
- entrypoint->valuesAtHead.argument(i).setType(m_graph, SpecCell);
- break;
- case FlushedJSValue:
- entrypoint->valuesAtHead.argument(i).makeBytecodeTop();
- break;
- default:
- DFG_CRASH(m_graph, nullptr, "Bad flush format for argument");
- break;
- }
}
+
for (size_t i = 0; i < entrypoint->valuesAtHead.numberOfLocals(); ++i) {
entrypoint->valuesAtHead.local(i).clear();
entrypoint->valuesAtTail.local(i).clear();
@@ -163,6 +166,7 @@
block->valuesAtTail.local(i).clear();
}
}
+
if (m_graph.m_form == SSA) {
for (BlockIndex blockIndex = 0; blockIndex < m_graph.numBlocks(); ++blockIndex) {
BasicBlock* block = m_graph.block(blockIndex);
@@ -371,6 +375,14 @@
changed |= merge(basicBlock, data->cases[i].target.block);
return changed;
}
+
+ case EntrySwitch: {
+ EntrySwitchData* data = ""
+ bool changed = false;
+ for (unsigned i = data->cases.size(); i--;)
+ changed |= merge(basicBlock, data->cases[i]);
+ return changed;
+ }
case Return:
case TailCall:
Modified: trunk/Source/_javascript_Core/dfg/DFGJITCode.cpp (221601 => 221602)
--- trunk/Source/_javascript_Core/dfg/DFGJITCode.cpp 2017-09-05 03:03:03 UTC (rev 221601)
+++ trunk/Source/_javascript_Core/dfg/DFGJITCode.cpp 2017-09-05 03:21:33 UTC (rev 221602)
@@ -63,7 +63,6 @@
common.shrinkToFit();
osrEntry.shrinkToFit();
osrExit.shrinkToFit();
- catchEntrypoints.shrinkToFit();
speculationRecovery.shrinkToFit();
minifiedDFG.prepareAndShrink();
variableEventStream.shrinkToFit();
@@ -243,7 +242,6 @@
auto comparator = [] (const auto& a, const auto& b) {
return a.m_bytecodeIndex < b.m_bytecodeIndex;
};
- std::sort(catchEntrypoints.begin(), catchEntrypoints.end(), comparator);
std::sort(osrEntry.begin(), osrEntry.end(), comparator);
#if !ASSERT_DISABLED
@@ -251,7 +249,6 @@
for (unsigned i = 0; i + 1 < osrVector.size(); ++i)
ASSERT(osrVector[i].m_bytecodeIndex <= osrVector[i + 1].m_bytecodeIndex);
};
- verifyIsSorted(catchEntrypoints);
verifyIsSorted(osrEntry);
#endif
}
Modified: trunk/Source/_javascript_Core/dfg/DFGJITCode.h (221601 => 221602)
--- trunk/Source/_javascript_Core/dfg/DFGJITCode.h 2017-09-05 03:03:03 UTC (rev 221601)
+++ trunk/Source/_javascript_Core/dfg/DFGJITCode.h 2017-09-05 03:21:33 UTC (rev 221602)
@@ -70,20 +70,8 @@
getOSREntryDataBytecodeIndex);
}
- CatchEntrypointData* catchOSREntryDataForBytecodeIndex(unsigned bytecodeIndex)
- {
- return tryBinarySearch<CatchEntrypointData, unsigned>(
- catchEntrypoints, catchEntrypoints.size(), bytecodeIndex,
- [] (const CatchEntrypointData* item) { return item->m_bytecodeIndex; });
- }
-
void finalizeOSREntrypoints();
- void appendCatchEntrypoint(unsigned bytecodeIndex, unsigned machineCodeOffset, Vector<FlushFormat>&& argumentFormats)
- {
- catchEntrypoints.append(CatchEntrypointData { bytecodeIndex, machineCodeOffset, WTFMove(argumentFormats) });
- }
-
unsigned appendOSRExit(const OSRExit& exit)
{
unsigned result = osrExit.size();
@@ -146,12 +134,10 @@
public:
CommonData common;
Vector<DFG::OSREntryData> osrEntry;
- Vector<CatchEntrypointData> catchEntrypoints;
SegmentedVector<DFG::OSRExit, 8> osrExit;
Vector<DFG::SpeculationRecovery> speculationRecovery;
DFG::VariableEventStream variableEventStream;
DFG::MinifiedGraph minifiedDFG;
- ScratchBuffer* catchOSREntryBuffer;
#if ENABLE(FTL_JIT)
uint8_t neverExecutedEntry { 1 };
Modified: trunk/Source/_javascript_Core/dfg/DFGJITCompiler.cpp (221601 => 221602)
--- trunk/Source/_javascript_Core/dfg/DFGJITCompiler.cpp 2017-09-05 03:03:03 UTC (rev 221601)
+++ trunk/Source/_javascript_Core/dfg/DFGJITCompiler.cpp 2017-09-05 03:21:33 UTC (rev 221602)
@@ -559,7 +559,7 @@
{
RELEASE_ASSERT(basicBlock.isCatchEntrypoint);
RELEASE_ASSERT(basicBlock.intersectionOfCFAHasVisited); // An entrypoint is reachable by definition.
- m_jitCode->appendCatchEntrypoint(basicBlock.bytecodeBegin, linkBuffer.offsetOf(blockHead), WTFMove(argumentFormats));
+ m_jitCode->common.appendCatchEntrypoint(basicBlock.bytecodeBegin, linkBuffer.locationOf(blockHead).executableAddress(), WTFMove(argumentFormats));
}
void JITCompiler::noticeOSREntry(BasicBlock& basicBlock, JITCompiler::Label blockHead, LinkBuffer& linkBuffer)
@@ -687,7 +687,7 @@
{
if (m_graph.m_maxLocalsForCatchOSREntry) {
uint32_t numberOfLiveLocals = std::max(*m_graph.m_maxLocalsForCatchOSREntry, 1u); // Make sure we always allocate a non-null catchOSREntryBuffer.
- m_jitCode->catchOSREntryBuffer = vm()->scratchBufferForSize(sizeof(JSValue) * numberOfLiveLocals);
+ m_jitCode->common.catchOSREntryBuffer = vm()->scratchBufferForSize(sizeof(JSValue) * numberOfLiveLocals);
}
}
Modified: trunk/Source/_javascript_Core/dfg/DFGMayExit.cpp (221601 => 221602)
--- trunk/Source/_javascript_Core/dfg/DFGMayExit.cpp 2017-09-05 03:03:03 UTC (rev 221601)
+++ trunk/Source/_javascript_Core/dfg/DFGMayExit.cpp 2017-09-05 03:21:33 UTC (rev 221602)
@@ -53,6 +53,7 @@
case LazyJSConstant:
case Int52Constant:
case MovHint:
+ case InitializeEntrypointArguments:
case SetLocal:
case Flush:
case Phantom:
@@ -78,6 +79,7 @@
case PhantomLocal:
case CountExecution:
case Jump:
+ case EntrySwitch:
case Branch:
case Unreachable:
case DoubleRep:
Modified: trunk/Source/_javascript_Core/dfg/DFGNode.h (221601 => 221602)
--- trunk/Source/_javascript_Core/dfg/DFGNode.h 2017-09-05 03:03:03 UTC (rev 221601)
+++ trunk/Source/_javascript_Core/dfg/DFGNode.h 2017-09-05 03:21:33 UTC (rev 221602)
@@ -203,6 +203,10 @@
bool didUseJumpTable;
};
+struct EntrySwitchData {
+ Vector<BasicBlock*> cases;
+};
+
struct CallVarargsData {
int firstVarArgOffset;
};
@@ -1315,6 +1319,11 @@
return op() == Switch;
}
+ bool isEntrySwitch() const
+ {
+ return op() == EntrySwitch;
+ }
+
bool isTerminal()
{
switch (op()) {
@@ -1321,6 +1330,7 @@
case Jump:
case Branch:
case Switch:
+ case EntrySwitch:
case Return:
case TailCall:
case DirectTailCall:
@@ -1382,6 +1392,12 @@
ASSERT(isSwitch());
return m_opInfo.as<SwitchData*>();
}
+
+ EntrySwitchData* entrySwitchData()
+ {
+ ASSERT(isEntrySwitch());
+ return m_opInfo.as<EntrySwitchData*>();
+ }
unsigned numSuccessors()
{
@@ -1392,6 +1408,8 @@
return 2;
case Switch:
return switchData()->cases.size() + 1;
+ case EntrySwitch:
+ return entrySwitchData()->cases.size();
default:
return 0;
}
@@ -1404,7 +1422,8 @@
return switchData()->cases[index].target.block;
RELEASE_ASSERT(index == switchData()->cases.size());
return switchData()->fallThrough.block;
- }
+ } else if (isEntrySwitch())
+ return entrySwitchData()->cases[index];
switch (index) {
case 0:
@@ -2013,6 +2032,12 @@
return m_opInfo.as<Profiler::ExecutionCounter*>();
}
+ unsigned entrypointIndex()
+ {
+ ASSERT(op() == InitializeEntrypointArguments);
+ return m_opInfo.as<unsigned>();
+ }
+
bool shouldGenerate()
{
return m_refCount;
Modified: trunk/Source/_javascript_Core/dfg/DFGNodeType.h (221601 => 221602)
--- trunk/Source/_javascript_Core/dfg/DFGNodeType.h 2017-09-05 03:03:03 UTC (rev 221601)
+++ trunk/Source/_javascript_Core/dfg/DFGNodeType.h 2017-09-05 03:21:33 UTC (rev 221602)
@@ -390,6 +390,7 @@
macro(Jump, NodeMustGenerate) \
macro(Branch, NodeMustGenerate) \
macro(Switch, NodeMustGenerate) \
+ macro(EntrySwitch, NodeMustGenerate) \
macro(Return, NodeMustGenerate) \
macro(TailCall, NodeMustGenerate | NodeHasVarArgs) \
macro(DirectTailCall, NodeMustGenerate | NodeHasVarArgs) \
@@ -440,6 +441,10 @@
/* Nodes for DOM JIT */\
macro(CallDOMGetter, NodeResultJS | NodeMustGenerate) \
macro(CallDOM, NodeResultJS | NodeMustGenerate) \
+ /* Metadata node that initializes the state for flushed argument types at an entrypoint in the program. */ \
+ /* Currently, we only use this for the blocks an EntrySwitch branches to at the root of the program. */ \
+ /* This is only used in SSA. */ \
+ macro(InitializeEntrypointArguments, NodeMustGenerate)
// This enum generates a monotonically increasing id for all Node types,
// and is used by the subsequent enum to fill out the id (as accessed via the NodeIdMask).
Modified: trunk/Source/_javascript_Core/dfg/DFGOSRAvailabilityAnalysisPhase.cpp (221601 => 221602)
--- trunk/Source/_javascript_Core/dfg/DFGOSRAvailabilityAnalysisPhase.cpp 2017-09-05 03:03:03 UTC (rev 221601)
+++ trunk/Source/_javascript_Core/dfg/DFGOSRAvailabilityAnalysisPhase.cpp 2017-09-05 03:21:33 UTC (rev 221602)
@@ -57,13 +57,10 @@
BasicBlock* root = m_graph.block(0);
root->ssa->availabilityAtHead.m_locals.fill(Availability::unavailable());
- for (unsigned argument = m_graph.m_argumentFormats.size(); argument--;) {
- FlushedAt flushedAt = FlushedAt(
- m_graph.m_argumentFormats[argument],
- virtualRegisterForArgument(argument));
- root->ssa->availabilityAtHead.m_locals.argument(argument) = Availability(flushedAt);
- }
+ for (unsigned argument = 0; argument < m_graph.block(0)->valuesAtHead.numberOfArguments(); ++argument)
+ root->ssa->availabilityAtHead.m_locals.argument(argument) = Availability::unavailable();
+
// This could be made more efficient by processing blocks in reverse postorder.
LocalOSRAvailabilityCalculator calculator(m_graph);
@@ -86,10 +83,14 @@
block->ssa->availabilityAtTail = calculator.m_availability;
changed = true;
-
+
for (unsigned successorIndex = block->numSuccessors(); successorIndex--;) {
BasicBlock* successor = block->successor(successorIndex);
successor->ssa->availabilityAtHead.merge(calculator.m_availability);
+ }
+
+ for (unsigned successorIndex = block->numSuccessors(); successorIndex--;) {
+ BasicBlock* successor = block->successor(successorIndex);
successor->ssa->availabilityAtHead.pruneByLiveness(
m_graph, successor->at(0)->origin.forExit);
}
@@ -199,6 +200,16 @@
m_availability.m_locals.operand(node->unlinkedLocal()).setNodeUnavailable();
break;
}
+
+ case InitializeEntrypointArguments: {
+ unsigned entrypointIndex = node->entrypointIndex();
+ const Vector<FlushFormat>& argumentFormats = m_graph.m_argumentFormats[entrypointIndex];
+ for (unsigned argument = argumentFormats.size(); argument--; ) {
+ FlushedAt flushedAt = FlushedAt(argumentFormats[argument], virtualRegisterForArgument(argument));
+ m_availability.m_locals.argument(argument) = Availability(flushedAt);
+ }
+ break;
+ }
case LoadVarargs:
case ForwardVarargs: {
Modified: trunk/Source/_javascript_Core/dfg/DFGOSREntry.cpp (221601 => 221602)
--- trunk/Source/_javascript_Core/dfg/DFGOSREntry.cpp 2017-09-05 03:03:03 UTC (rev 221601)
+++ trunk/Source/_javascript_Core/dfg/DFGOSREntry.cpp 2017-09-05 03:21:33 UTC (rev 221602)
@@ -338,16 +338,19 @@
}
void* prepareCatchOSREntry(ExecState* exec, CodeBlock* codeBlock, unsigned bytecodeIndex)
-{
- if (!Options::useOSREntryToDFG())
+{
+ ASSERT(codeBlock->jitType() == JITCode::DFGJIT || codeBlock->jitType() == JITCode::FTLJIT);
+
+ if (!Options::useOSREntryToDFG() && codeBlock->jitCode()->jitType() == JITCode::DFGJIT)
return nullptr;
+ if (!Options::useOSREntryToFTL() && codeBlock->jitCode()->jitType() == JITCode::FTLJIT)
+ return nullptr;
VM& vm = exec->vm();
- ASSERT(codeBlock->jitType() == JITCode::DFGJIT);
- DFG::JITCode* jitCode = codeBlock->jitCode()->dfg();
- RELEASE_ASSERT(jitCode);
- DFG::CatchEntrypointData* catchEntrypoint = jitCode->catchOSREntryDataForBytecodeIndex(bytecodeIndex);
+ CommonData* dfgCommon = codeBlock->jitCode()->dfgCommon();
+ RELEASE_ASSERT(dfgCommon);
+ DFG::CatchEntrypointData* catchEntrypoint = dfgCommon->catchOSREntryDataForBytecodeIndex(bytecodeIndex);
if (!catchEntrypoint) {
// This can be null under some circumstances. The most common is that we didn't
// compile this op_catch as an entrypoint since it had never executed when starting
@@ -356,9 +359,9 @@
}
// We're only allowed to OSR enter if we've proven we have compatible argument types.
- for (unsigned argument = 0; argument < catchEntrypoint->m_argumentFormats.size(); ++argument) {
+ for (unsigned argument = 0; argument < catchEntrypoint->argumentFormats.size(); ++argument) {
JSValue value = exec->uncheckedR(virtualRegisterForArgument(argument)).jsValue();
- switch (catchEntrypoint->m_argumentFormats[argument]) {
+ switch (catchEntrypoint->argumentFormats[argument]) {
case DFG::FlushedInt32:
if (!value.isInt32())
return nullptr;
@@ -382,13 +385,13 @@
}
}
- unsigned frameSizeForCheck = jitCode->common.requiredRegisterCountForExecutionAndExit();
+ unsigned frameSizeForCheck = dfgCommon->requiredRegisterCountForExecutionAndExit();
if (UNLIKELY(!vm.ensureStackCapacityFor(&exec->registers()[virtualRegisterForLocal(frameSizeForCheck).offset()])))
return nullptr;
ASSERT(Interpreter::getOpcodeID(exec->codeBlock()->instructions()[exec->bytecodeOffset()].u.opcode) == op_catch);
ValueProfileAndOperandBuffer* buffer = static_cast<ValueProfileAndOperandBuffer*>(exec->codeBlock()->instructions()[exec->bytecodeOffset() + 3].u.pointer);
- JSValue* dataBuffer = reinterpret_cast<JSValue*>(jitCode->catchOSREntryBuffer->dataBuffer());
+ JSValue* dataBuffer = reinterpret_cast<JSValue*>(dfgCommon->catchOSREntryBuffer->dataBuffer());
unsigned index = 0;
buffer->forEach([&] (ValueProfileAndOperand& profile) {
if (!VirtualRegister(profile.m_operand).isLocal())
@@ -397,9 +400,8 @@
++index;
});
- jitCode->catchOSREntryBuffer->setActiveLength(sizeof(JSValue) * index);
-
- return jitCode->executableAddressAtOffset(catchEntrypoint->m_machineCodeOffset);
+ dfgCommon->catchOSREntryBuffer->setActiveLength(sizeof(JSValue) * index);
+ return catchEntrypoint->machineCode;
}
} } // namespace JSC::DFG
Modified: trunk/Source/_javascript_Core/dfg/DFGOSREntry.h (221601 => 221602)
--- trunk/Source/_javascript_Core/dfg/DFGOSREntry.h 2017-09-05 03:03:03 UTC (rev 221601)
+++ trunk/Source/_javascript_Core/dfg/DFGOSREntry.h 2017-09-05 03:21:33 UTC (rev 221602)
@@ -71,11 +71,11 @@
}
struct CatchEntrypointData {
- unsigned m_bytecodeIndex;
- unsigned m_machineCodeOffset;
// We use this when doing OSR entry at catch. We prove the arguments
// are of the expected type before entering at a catch block.
- Vector<FlushFormat> m_argumentFormats;
+ void* machineCode;
+ Vector<FlushFormat> argumentFormats;
+ unsigned bytecodeIndex;
};
// Returns a pointer to a data buffer that the OSR entry thunk will recognize and
Modified: trunk/Source/_javascript_Core/dfg/DFGOSREntrypointCreationPhase.cpp (221601 => 221602)
--- trunk/Source/_javascript_Core/dfg/DFGOSREntrypointCreationPhase.cpp 2017-09-05 03:03:03 UTC (rev 221601)
+++ trunk/Source/_javascript_Core/dfg/DFGOSREntrypointCreationPhase.cpp 2017-09-05 03:21:33 UTC (rev 221602)
@@ -147,8 +147,7 @@
m_graph.m_entrypointToArguments.clear();
m_graph.m_entrypointToArguments.add(newRoot, newArguments);
- m_graph.m_cpsCFG = std::make_unique<CPSCFG>(m_graph);
-
+ m_graph.invalidateCFG();
m_graph.resetReachability();
m_graph.killUnreachableBlocks();
Modified: trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp (221601 => 221602)
--- trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp 2017-09-05 03:03:03 UTC (rev 221601)
+++ trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp 2017-09-05 03:21:33 UTC (rev 221602)
@@ -1093,6 +1093,7 @@
RELEASE_ASSERT_NOT_REACHED();
break;
+ case EntrySwitch:
case Upsilon:
// These don't get inserted until we go into SSA.
RELEASE_ASSERT_NOT_REACHED();
@@ -1156,6 +1157,7 @@
case ForwardVarargs:
case PutDynamicVar:
case NukeStructureAndSetButterfly:
+ case InitializeEntrypointArguments:
break;
// This gets ignored because it only pretends to produce a value.
Modified: trunk/Source/_javascript_Core/dfg/DFGSSAConversionPhase.cpp (221601 => 221602)
--- trunk/Source/_javascript_Core/dfg/DFGSSAConversionPhase.cpp 2017-09-05 03:03:03 UTC (rev 221601)
+++ trunk/Source/_javascript_Core/dfg/DFGSSAConversionPhase.cpp 2017-09-05 03:21:33 UTC (rev 221602)
@@ -29,6 +29,7 @@
#if ENABLE(DFG_JIT)
#include "DFGBasicBlockInlines.h"
+#include "DFGBlockInsertionSet.h"
#include "DFGGraph.h"
#include "DFGInsertionSet.h"
#include "DFGPhase.h"
@@ -44,7 +45,6 @@
public:
SSAConversionPhase(Graph& graph)
: Phase(graph, "SSA conversion")
- , m_calculator(graph)
, m_insertionSet(graph)
{
}
@@ -52,9 +52,59 @@
bool run()
{
RELEASE_ASSERT(m_graph.m_form == ThreadedCPS);
+ RELEASE_ASSERT(!m_graph.m_isInSSAConversion);
+ m_graph.m_isInSSAConversion = true;
m_graph.clearReplacements();
m_graph.clearCPSCFGData();
+
+ HashMap<unsigned, BasicBlock*, WTF::IntHash<unsigned>, WTF::UnsignedWithZeroKeyHashTraits<unsigned>> entrypointIndexToArgumentsBlock;
+
+ {
+ m_graph.m_numberOfEntrypoints = m_graph.m_entrypoints.size();
+
+ BlockInsertionSet blockInsertionSet(m_graph);
+ BasicBlock* newRoot = blockInsertionSet.insert(0, 1.0f);
+
+ EntrySwitchData* entrySwitchData = m_graph.m_entrySwitchData.add();
+ for (unsigned entrypointIndex = 0; entrypointIndex < m_graph.m_numberOfEntrypoints; ++entrypointIndex) {
+ BasicBlock* oldRoot = m_graph.m_entrypoints[entrypointIndex];
+ entrypointIndexToArgumentsBlock.add(entrypointIndex, oldRoot);
+ entrySwitchData->cases.append(oldRoot);
+
+ ASSERT(oldRoot->predecessors.isEmpty());
+ oldRoot->predecessors.append(newRoot);
+
+ if (oldRoot->isCatchEntrypoint) {
+ ASSERT(!!entrypointIndex);
+ m_graph.m_entrypointIndexToCatchBytecodeOffset.add(entrypointIndex, oldRoot->bytecodeBegin);
+ }
+
+ NodeOrigin origin = oldRoot->at(0)->origin;
+ m_insertionSet.insertNode(
+ 0, SpecNone, InitializeEntrypointArguments, origin, OpInfo(entrypointIndex));
+ m_insertionSet.insertNode(
+ 0, SpecNone, ExitOK, origin);
+ m_insertionSet.execute(oldRoot);
+ }
+
+ RELEASE_ASSERT(entrySwitchData->cases[0] == m_graph.block(0)); // We strongly assume the normal call entrypoint is the first item in the list.
+
+ m_graph.m_argumentFormats.resize(m_graph.m_numberOfEntrypoints);
+
+ const bool exitOK = false;
+ NodeOrigin origin { CodeOrigin(0), CodeOrigin(0), exitOK };
+ newRoot->appendNode(
+ m_graph, SpecNone, EntrySwitch, origin, OpInfo(entrySwitchData));
+
+ m_graph.m_entrypoints.clear();
+ m_graph.m_entrypoints.append(newRoot);
+
+ blockInsertionSet.execute();
+ }
+
+ SSACalculator calculator(m_graph);
+
m_graph.ensureSSADominators();
if (verbose) {
@@ -67,7 +117,7 @@
if (!variable.isRoot())
continue;
- SSACalculator::Variable* ssaVariable = m_calculator.newVariable();
+ SSACalculator::Variable* ssaVariable = calculator.newVariable();
ASSERT(ssaVariable->index() == m_variableForSSAIndex.size());
m_variableForSSAIndex.append(&variable);
m_ssaVariableForVariable.add(&variable, ssaVariable);
@@ -103,7 +153,7 @@
m_argumentMapping.add(node, childNode);
}
- m_calculator.newDef(
+ calculator.newDef(
m_ssaVariableForVariable.get(variable), block, childNode);
}
@@ -112,7 +162,7 @@
// Decide where Phis are to be inserted. This creates the Phi's but doesn't insert them
// yet. We will later know where to insert based on where SSACalculator tells us to.
- m_calculator.computePhis(
+ calculator.computePhis(
[&] (SSACalculator::Variable* ssaVariable, BasicBlock* block) -> Node* {
VariableAccessData* variable = m_variableForSSAIndex[ssaVariable->index()];
@@ -167,7 +217,7 @@
for (unsigned i = 0; i < m_variableForSSAIndex.size(); ++i)
dataLog(" ", i, ": ", VariableAccessDataDump(m_graph, m_variableForSSAIndex[i]), "\n");
dataLog("\n");
- dataLog("SSA calculator: ", m_calculator, "\n");
+ dataLog("SSA calculator: ", calculator, "\n");
}
// Do the bulk of the SSA conversion. For each block, this tracks the operand->Node
@@ -220,7 +270,7 @@
dataLog("Considering live variable ", VariableAccessDataDump(m_graph, variable), " at head of block ", *block, "\n");
SSACalculator::Variable* ssaVariable = m_ssaVariableForVariable.get(variable);
- SSACalculator::Def* def = m_calculator.reachingDefAtHead(block, ssaVariable);
+ SSACalculator::Def* def = calculator.reachingDefAtHead(block, ssaVariable);
if (!def) {
// If we are required to insert a Phi, then we won't have a reaching def
// at head.
@@ -246,7 +296,7 @@
// valueForOperand with those Phis. For Phis associated with variables that are not
// flushed, we also insert a MovHint.
size_t phiInsertionPoint = 0;
- for (SSACalculator::Def* phiDef : m_calculator.phisForBlock(block)) {
+ for (SSACalculator::Def* phiDef : calculator.phisForBlock(block)) {
VariableAccessData* variable = m_variableForSSAIndex[phiDef->variable()->index()];
m_insertionSet.insert(phiInsertionPoint, phiDef->value());
@@ -346,7 +396,7 @@
NodeOrigin upsilonOrigin = terminal.node->origin;
for (unsigned successorIndex = block->numSuccessors(); successorIndex--;) {
BasicBlock* successorBlock = block->successor(successorIndex);
- for (SSACalculator::Def* phiDef : m_calculator.phisForBlock(successorBlock)) {
+ for (SSACalculator::Def* phiDef : calculator.phisForBlock(successorBlock)) {
Node* phiNode = phiDef->value();
SSACalculator::Variable* ssaVariable = phiDef->variable();
VariableAccessData* variable = m_variableForSSAIndex[ssaVariable->index()];
@@ -383,23 +433,25 @@
block->ssa = std::make_unique<BasicBlock::SSAData>(block);
}
- // FIXME: Support multiple entrypoints in DFG SSA:
- // https://bugs.webkit.org/show_bug.cgi?id=175396
- RELEASE_ASSERT(m_graph.m_entrypoints.size() == 1);
- auto& arguments = m_graph.m_entrypointToArguments.find(m_graph.block(0))->value;
- m_graph.m_argumentFormats.resize(arguments.size());
- for (unsigned i = arguments.size(); i--;) {
- FlushFormat format = FlushedJSValue;
+ for (auto& pair : entrypointIndexToArgumentsBlock) {
+ unsigned entrypointIndex = pair.key;
+ BasicBlock* oldRoot = pair.value;
+ ArgumentsVector& arguments = m_graph.m_entrypointToArguments.find(oldRoot)->value;
+ Vector<FlushFormat> argumentFormats;
+ argumentFormats.reserveInitialCapacity(arguments.size());
+ for (unsigned i = 0; i < arguments.size(); ++i) {
+ Node* node = m_argumentMapping.get(arguments[i]);
+ RELEASE_ASSERT(node);
+ argumentFormats.uncheckedAppend(node->stackAccessData()->format);
+ }
+ m_graph.m_argumentFormats[entrypointIndex] = WTFMove(argumentFormats);
+ }
- Node* node = m_argumentMapping.get(arguments[i]);
-
- RELEASE_ASSERT(node);
- format = node->stackAccessData()->format;
-
- m_graph.m_argumentFormats[i] = format;
- arguments[i] = node; // Record the load that loads the arguments for the benefit of exit profiling.
- }
-
+ m_graph.m_entrypointToArguments.clear();
+
+ RELEASE_ASSERT(m_graph.m_isInSSAConversion);
+ m_graph.m_isInSSAConversion = false;
+
m_graph.m_form = SSA;
if (verbose) {
@@ -411,7 +463,6 @@
}
private:
- SSACalculator m_calculator;
InsertionSet m_insertionSet;
HashMap<VariableAccessData*, SSACalculator::Variable*> m_ssaVariableForVariable;
HashMap<Node*, Node*> m_argumentMapping;
@@ -421,11 +472,7 @@
bool performSSAConversion(Graph& graph)
{
- RELEASE_ASSERT(!graph.m_isInSSAConversion);
- graph.m_isInSSAConversion = true;
bool result = runPhase<SSAConversionPhase>(graph);
- RELEASE_ASSERT(graph.m_isInSSAConversion);
- graph.m_isInSSAConversion = false;
return result;
}
Modified: trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h (221601 => 221602)
--- trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h 2017-09-05 03:03:03 UTC (rev 221601)
+++ trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h 2017-09-05 03:21:33 UTC (rev 221602)
@@ -311,6 +311,7 @@
case Jump:
case Branch:
case Switch:
+ case EntrySwitch:
case Return:
case TailCall:
case DirectTailCall:
@@ -399,6 +400,7 @@
case AtomicsSub:
case AtomicsXor:
case AtomicsIsLockFree:
+ case InitializeEntrypointArguments:
return true;
case ArraySlice:
Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp (221601 => 221602)
--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp 2017-09-05 03:03:03 UTC (rev 221601)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp 2017-09-05 03:21:33 UTC (rev 221602)
@@ -1931,6 +1931,7 @@
}
m_jit.jitCode()->finalizeOSREntrypoints();
+ m_jit.jitCode()->common.finalizeCatchEntrypoints();
ASSERT(osrEntryIndex == m_osrEntryHeads.size());
Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp (221601 => 221602)
--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp 2017-09-05 03:03:03 UTC (rev 221601)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp 2017-09-05 03:21:33 UTC (rev 221602)
@@ -5650,7 +5650,7 @@
GPRReg tagGPR = tag.gpr();
GPRReg payloadGPR = payload.gpr();
- JSValue* ptr = &reinterpret_cast<JSValue*>(m_jit.jitCode()->catchOSREntryBuffer->dataBuffer())[node->catchOSREntryIndex()];
+ JSValue* ptr = &reinterpret_cast<JSValue*>(m_jit.jitCode()->common.catchOSREntryBuffer->dataBuffer())[node->catchOSREntryIndex()];
m_jit.move(CCallHelpers::TrustedImmPtr(ptr), tempGPR);
m_jit.load32(CCallHelpers::Address(tempGPR, TagOffset), tagGPR);
m_jit.load32(CCallHelpers::Address(tempGPR, PayloadOffset), payloadGPR);
@@ -5703,6 +5703,8 @@
case AtomicsSub:
case AtomicsXor:
case IdentityWithProfile:
+ case InitializeEntrypointArguments:
+ case EntrySwitch:
DFG_CRASH(m_jit.graph(), node, "unexpected node in DFG backend");
break;
}
Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp (221601 => 221602)
--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp 2017-09-05 03:03:03 UTC (rev 221601)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp 2017-09-05 03:21:33 UTC (rev 221602)
@@ -6006,7 +6006,7 @@
break;
case ExtractCatchLocal: {
- JSValue* ptr = &reinterpret_cast<JSValue*>(m_jit.jitCode()->catchOSREntryBuffer->dataBuffer())[node->catchOSREntryIndex()];
+ JSValue* ptr = &reinterpret_cast<JSValue*>(m_jit.jitCode()->common.catchOSREntryBuffer->dataBuffer())[node->catchOSREntryIndex()];
GPRTemporary temp(this);
GPRReg tempGPR = temp.gpr();
m_jit.move(CCallHelpers::TrustedImmPtr(ptr), tempGPR);
@@ -6113,6 +6113,8 @@
#endif // ENABLE(FTL_JIT)
case LastNodeType:
+ case EntrySwitch:
+ case InitializeEntrypointArguments:
case Phi:
case Upsilon:
case ExtractOSREntryLocal:
Modified: trunk/Source/_javascript_Core/dfg/DFGStaticExecutionCountEstimationPhase.cpp (221601 => 221602)
--- trunk/Source/_javascript_Core/dfg/DFGStaticExecutionCountEstimationPhase.cpp 2017-09-05 03:03:03 UTC (rev 221601)
+++ trunk/Source/_javascript_Core/dfg/DFGStaticExecutionCountEstimationPhase.cpp 2017-09-05 03:21:33 UTC (rev 221602)
@@ -80,6 +80,11 @@
applyCounts(data->fallThrough);
break;
}
+
+ case EntrySwitch: {
+ DFG_CRASH(m_graph, terminal, "Unexpected EntrySwitch in CPS form.");
+ break;
+ }
default:
break;
Modified: trunk/Source/_javascript_Core/dfg/DFGValidate.cpp (221601 => 221602)
--- trunk/Source/_javascript_Core/dfg/DFGValidate.cpp 2017-09-05 03:03:03 UTC (rev 221601)
+++ trunk/Source/_javascript_Core/dfg/DFGValidate.cpp 2017-09-05 03:21:33 UTC (rev 221602)
@@ -84,7 +84,11 @@
// in release builds.
VALIDATE((m_graph.block(0)), m_graph.isEntrypoint(m_graph.block(0)));
+ VALIDATE((m_graph.block(0)), m_graph.block(0) == m_graph.m_entrypoints[0]);
+ for (BasicBlock* block : m_graph.m_entrypoints)
+ VALIDATE((block), block->predecessors.isEmpty());
+
// Validate that all local variables at the head of all entrypoints are dead.
for (BasicBlock* entrypoint : m_graph.m_entrypoints) {
for (unsigned i = 0; i < entrypoint->variablesAtHead.numberOfLocals(); ++i)
@@ -549,6 +553,8 @@
case PutStack:
case KillStack:
case GetStack:
+ case EntrySwitch:
+ case InitializeEntrypointArguments:
VALIDATE((node), !"unexpected node type in CPS");
break;
case MaterializeNewObject: {
@@ -639,6 +645,13 @@
// FIXME: Add more things here.
// https://bugs.webkit.org/show_bug.cgi?id=123471
+ VALIDATE((), m_graph.m_entrypoints.size() == 1);
+ VALIDATE((), m_graph.m_entrypoints[0] == m_graph.block(0));
+ VALIDATE((), !m_graph.m_argumentFormats.isEmpty()); // We always have at least one entrypoint.
+
+ for (unsigned entrypointIndex : m_graph.m_entrypointIndexToCatchBytecodeOffset.keys())
+ VALIDATE((), entrypointIndex > 0); // By convention, 0 is the entrypoint index for the op_enter entrypoint, which can not be in a catch.
+
for (BlockIndex blockIndex = 0; blockIndex < m_graph.numBlocks(); ++blockIndex) {
BasicBlock* block = m_graph.block(blockIndex);
if (!block)
@@ -740,6 +753,14 @@
break;
}
+ case EntrySwitch:
+ VALIDATE((node), node->entrySwitchData()->cases.size() == m_graph.m_numberOfEntrypoints);
+ break;
+
+ case InitializeEntrypointArguments:
+ VALIDATE((node), node->entrypointIndex() < m_graph.m_numberOfEntrypoints);
+ break;
+
default:
m_graph.doToChildren(
node,
@@ -780,6 +801,8 @@
getLocalPositions.operand(operand) < setLocalPositions.operand(operand));
}
+ void reportValidationContext() { }
+
void reportValidationContext(Node* node)
{
dataLogF("@%u", node->index());
Modified: trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp (221601 => 221602)
--- trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp 2017-09-05 03:03:03 UTC (rev 221601)
+++ trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp 2017-09-05 03:21:33 UTC (rev 221602)
@@ -111,6 +111,7 @@
case Phi:
case Upsilon:
case ExtractOSREntryLocal:
+ case ExtractCatchLocal:
case LoopHint:
case SkipScope:
case GetGlobalObject:
@@ -245,6 +246,7 @@
case GetMyArgumentByVal:
case GetMyArgumentByValOutOfBounds:
case ForwardVarargs:
+ case EntrySwitch:
case Switch:
case TypeOf:
case PutById:
@@ -301,6 +303,7 @@
case AtomicsSub:
case AtomicsXor:
case AtomicsIsLockFree:
+ case InitializeEntrypointArguments:
// These are OK.
break;
Modified: trunk/Source/_javascript_Core/ftl/FTLCompile.cpp (221601 => 221602)
--- trunk/Source/_javascript_Core/ftl/FTLCompile.cpp 2017-09-05 03:03:03 UTC (rev 221601)
+++ trunk/Source/_javascript_Core/ftl/FTLCompile.cpp 2017-09-05 03:21:33 UTC (rev 221602)
@@ -142,6 +142,7 @@
});
state.finalizer->b3CodeLinkBuffer = std::make_unique<LinkBuffer>(jit, codeBlock, JITCompilationCanFail);
+
if (state.finalizer->b3CodeLinkBuffer->didFailToAllocate()) {
state.allocationFailed = true;
return;
@@ -152,9 +153,18 @@
codeBlock->setPCToCodeOriginMap(std::make_unique<PCToCodeOriginMap>(PCToCodeOriginMapBuilder(vm, WTFMove(originMap)), *state.finalizer->b3CodeLinkBuffer));
state.generatedFunction = bitwise_cast<GeneratedFunction>(
- state.finalizer->b3CodeLinkBuffer->entrypoint().executableAddress());
+ state.finalizer->b3CodeLinkBuffer->locationOf(state.proc->entrypointLabel(0)));
state.jitCode->initializeB3Byproducts(state.proc->releaseByproducts());
+ for (auto pair : state.graph.m_entrypointIndexToCatchBytecodeOffset) {
+ unsigned catchBytecodeOffset = pair.value;
+ unsigned entrypointIndex = pair.key;
+ Vector<FlushFormat> argumentFormats = state.graph.m_argumentFormats[entrypointIndex];
+ state.jitCode->common.appendCatchEntrypoint(
+ catchBytecodeOffset, state.finalizer->b3CodeLinkBuffer->locationOf(state.proc->entrypointLabel(entrypointIndex)).executableAddress(), WTFMove(argumentFormats));
+ }
+ state.jitCode->common.finalizeCatchEntrypoints();
+
if (B3::Air::Disassembler* disassembler = state.proc->code().disassembler()) {
PrintStream& out = WTF::dataFile();
Modified: trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp (221601 => 221602)
--- trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp 2017-09-05 03:03:03 UTC (rev 221601)
+++ trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp 2017-09-05 03:21:33 UTC (rev 221602)
@@ -28,6 +28,7 @@
#if ENABLE(FTL_JIT)
+#include "AirCode.h"
#include "AirGenerationContext.h"
#include "AllowMacroScratchRegisterUsage.h"
#include "AtomicsObject.h"
@@ -151,6 +152,29 @@
"_", codeBlock()->hash());
} else
name = "jsBody";
+
+ {
+ m_proc.setNumEntrypoints(m_graph.m_numberOfEntrypoints);
+ CodeBlock* codeBlock = m_graph.m_codeBlock;
+
+ RefPtr<B3::Air::PrologueGenerator> catchPrologueGenerator = createSharedTask<B3::Air::PrologueGeneratorFunction>(
+ [codeBlock] (CCallHelpers& jit, B3::Air::Code& code) {
+ AllowMacroScratchRegisterUsage allowScratch(jit);
+ jit.addPtr(CCallHelpers::TrustedImm32(-code.frameSize()), GPRInfo::callFrameRegister, CCallHelpers::stackPointerRegister);
+ jit.emitSave(code.calleeSaveRegisterAtOffsetList());
+ jit.emitPutToCallFrameHeader(codeBlock, CallFrameSlot::codeBlock);
+ });
+
+ for (unsigned catchEntrypointIndex : m_graph.m_entrypointIndexToCatchBytecodeOffset.keys()) {
+ RELEASE_ASSERT(catchEntrypointIndex != 0);
+ m_proc.code().setPrologueForEntrypoint(catchEntrypointIndex, catchPrologueGenerator);
+ }
+
+ if (m_graph.m_maxLocalsForCatchOSREntry) {
+ uint32_t numberOfLiveLocals = std::max(*m_graph.m_maxLocalsForCatchOSREntry, 1u); // Make sure we always allocate a non-null catchOSREntryBuffer.
+ m_ftlState.jitCode->common.catchOSREntryBuffer = m_graph.m_vm.scratchBufferForSize(sizeof(JSValue) * numberOfLiveLocals);
+ }
+ }
m_graph.ensureSSADominators();
@@ -162,7 +186,8 @@
// We use prologue frequency for all of the initialization code.
m_out.setFrequency(1);
- m_prologue = m_out.newBlock();
+ LBasicBlock prologue = m_out.newBlock();
+ LBasicBlock callEntrypointArgumentSpeculations = m_out.newBlock();
m_handleExceptions = m_out.newBlock();
for (BlockIndex blockIndex = 0; blockIndex < m_graph.numBlocks(); ++blockIndex) {
@@ -176,8 +201,8 @@
// Back to prologue frequency for any bocks that get sneakily created in the initialization code.
m_out.setFrequency(1);
- m_out.appendTo(m_prologue, m_handleExceptions);
- m_out.initializeConstants(m_proc, m_prologue);
+ m_out.appendTo(prologue, callEntrypointArgumentSpeculations);
+ m_out.initializeConstants(m_proc, prologue);
createPhiVariables();
size_t sizeOfCaptured = sizeof(JSValue) * m_graph.m_nextMachineLocal;
@@ -259,52 +284,58 @@
});
LBasicBlock firstDFGBasicBlock = lowBlock(m_graph.block(0));
- // Check Arguments.
- availabilityMap().clear();
- availabilityMap().m_locals = Operands<Availability>(codeBlock()->numParameters(), 0);
- for (unsigned i = codeBlock()->numParameters(); i--;) {
- availabilityMap().m_locals.argument(i) =
- Availability(FlushedAt(FlushedJSValue, virtualRegisterForArgument(i)));
- }
- m_node = nullptr;
- m_origin = NodeOrigin(CodeOrigin(0), CodeOrigin(0), true);
- auto& arguments = m_graph.m_entrypointToArguments.find(m_graph.block(0))->value;
- for (unsigned i = codeBlock()->numParameters(); i--;) {
- Node* node = arguments[i];
- m_out.setOrigin(node);
- VirtualRegister operand = virtualRegisterForArgument(i);
-
- LValue jsValue = m_out.load64(addressFor(operand));
-
- if (node) {
- DFG_ASSERT(m_graph, node, operand == node->stackAccessData()->machineLocal);
+
+ {
+ Vector<LBasicBlock> successors(m_graph.m_numberOfEntrypoints);
+ successors[0] = callEntrypointArgumentSpeculations;
+ for (unsigned i = 1; i < m_graph.m_numberOfEntrypoints; ++i) {
+ // Currently, the only other entrypoint is an op_catch entrypoint.
+ // We do OSR entry at op_catch, and we prove argument formats before
+ // jumping to FTL code, so we don't need to check argument types here
+ // for these entrypoints.
+ successors[i] = firstDFGBasicBlock;
+ }
+
+ m_out.entrySwitch(successors);
+ m_out.appendTo(callEntrypointArgumentSpeculations, m_handleExceptions);
+
+ m_node = nullptr;
+ m_origin = NodeOrigin(CodeOrigin(0), CodeOrigin(0), true);
+
+ // Check Arguments.
+ availabilityMap().clear();
+ availabilityMap().m_locals = Operands<Availability>(codeBlock()->numParameters(), 0);
+ for (unsigned i = codeBlock()->numParameters(); i--;) {
+ availabilityMap().m_locals.argument(i) =
+ Availability(FlushedAt(FlushedJSValue, virtualRegisterForArgument(i)));
+ }
+
+ for (unsigned i = codeBlock()->numParameters(); i--;) {
+ MethodOfGettingAValueProfile profile(&m_graph.m_profiledBlock->valueProfileForArgument(i));
+ VirtualRegister operand = virtualRegisterForArgument(i);
+ LValue jsValue = m_out.load64(addressFor(operand));
- // This is a hack, but it's an effective one. It allows us to do CSE on the
- // primordial load of arguments. This assumes that the GetLocal that got put in
- // place of the original SetArgument doesn't have any effects before it. This
- // should hold true.
- m_loadedArgumentValues.add(node, jsValue);
+ switch (m_graph.m_argumentFormats[0][i]) {
+ case FlushedInt32:
+ speculate(BadType, jsValueValue(jsValue), profile, isNotInt32(jsValue));
+ break;
+ case FlushedBoolean:
+ speculate(BadType, jsValueValue(jsValue), profile, isNotBoolean(jsValue));
+ break;
+ case FlushedCell:
+ speculate(BadType, jsValueValue(jsValue), profile, isNotCell(jsValue));
+ break;
+ case FlushedJSValue:
+ break;
+ default:
+ DFG_CRASH(m_graph, nullptr, "Bad flush format for argument");
+ break;
+ }
}
-
- switch (m_graph.m_argumentFormats[i]) {
- case FlushedInt32:
- speculate(BadType, jsValueValue(jsValue), node, isNotInt32(jsValue));
- break;
- case FlushedBoolean:
- speculate(BadType, jsValueValue(jsValue), node, isNotBoolean(jsValue));
- break;
- case FlushedCell:
- speculate(BadType, jsValueValue(jsValue), node, isNotCell(jsValue));
- break;
- case FlushedJSValue:
- break;
- default:
- DFG_CRASH(m_graph, node, "Bad flush format for argument");
- break;
- }
+ m_out.jump(firstDFGBasicBlock);
}
- m_out.jump(firstDFGBasicBlock);
+
m_out.appendTo(m_handleExceptions, firstDFGBasicBlock);
Box<CCallHelpers::Label> exceptionHandler = state->exceptionHandler;
m_out.patchpoint(Void)->setGenerator(
@@ -512,6 +543,9 @@
case ExtractOSREntryLocal:
compileExtractOSREntryLocal();
break;
+ case ExtractCatchLocal:
+ compileExtractCatchLocal();
+ break;
case GetStack:
compileGetStack();
break;
@@ -940,6 +974,9 @@
case DFG::Switch:
compileSwitch();
break;
+ case DFG::EntrySwitch:
+ compileEntrySwitch();
+ break;
case DFG::Return:
compileReturn();
break;
@@ -1155,6 +1192,7 @@
case PutHint:
case BottomValue:
case KillStack:
+ case InitializeEntrypointArguments:
break;
default:
DFG_CRASH(m_graph, m_node, "Unrecognized node in FTL backend");
@@ -1525,16 +1563,15 @@
m_ftlState.jitCode->ftlForOSREntry()->entryBuffer()->dataBuffer());
setJSValue(m_out.load64(m_out.absolute(buffer + m_node->unlinkedLocal().toLocal())));
}
+
+ void compileExtractCatchLocal()
+ {
+ EncodedJSValue* buffer = static_cast<EncodedJSValue*>(m_ftlState.jitCode->common.catchOSREntryBuffer->dataBuffer());
+ setJSValue(m_out.load64(m_out.absolute(buffer + m_node->catchOSREntryIndex())));
+ }
void compileGetStack()
{
- // GetLocals arise only for captured variables and arguments. For arguments, we might have
- // already loaded it.
- if (LValue value = m_loadedArgumentValues.get(m_node)) {
- setJSValue(value);
- return;
- }
-
StackAccessData* data = ""
AbstractValue& value = m_state.variables().operand(data->local);
@@ -7791,6 +7828,14 @@
DFG_CRASH(m_graph, m_node, "Bad switch kind");
}
+
+ void compileEntrySwitch()
+ {
+ Vector<LBasicBlock> successors;
+ for (DFG::BasicBlock* successor : m_node->entrySwitchData()->cases)
+ successors.append(lowBlock(successor));
+ m_out.entrySwitch(successors);
+ }
void compileReturn()
{
@@ -12611,6 +12656,12 @@
{
appendOSRExit(kind, lowValue, highValue, failCondition, m_origin);
}
+
+ void speculate(
+ ExitKind kind, FormattedValue lowValue, const MethodOfGettingAValueProfile& profile, LValue failCondition)
+ {
+ appendOSRExit(kind, lowValue, profile, failCondition, m_origin);
+ }
void terminate(ExitKind kind)
{
@@ -14135,16 +14186,29 @@
OSRExitDescriptor* appendOSRExitDescriptor(FormattedValue lowValue, Node* highValue)
{
+ return appendOSRExitDescriptor(lowValue, m_graph.methodOfGettingAValueProfileFor(m_node, highValue));
+ }
+
+ OSRExitDescriptor* appendOSRExitDescriptor(FormattedValue lowValue, const MethodOfGettingAValueProfile& profile)
+ {
return &m_ftlState.jitCode->osrExitDescriptors.alloc(
- lowValue.format(), m_graph.methodOfGettingAValueProfileFor(m_node, highValue),
+ lowValue.format(), profile,
availabilityMap().m_locals.numberOfArguments(),
availabilityMap().m_locals.numberOfLocals());
}
-
+
void appendOSRExit(
ExitKind kind, FormattedValue lowValue, Node* highValue, LValue failCondition,
NodeOrigin origin, bool isExceptionHandler = false)
{
+ return appendOSRExit(kind, lowValue, m_graph.methodOfGettingAValueProfileFor(m_node, highValue),
+ failCondition, origin, isExceptionHandler);
+ }
+
+ void appendOSRExit(
+ ExitKind kind, FormattedValue lowValue, const MethodOfGettingAValueProfile& profile, LValue failCondition,
+ NodeOrigin origin, bool isExceptionHandler = false)
+ {
if (verboseCompilationEnabled()) {
dataLog(" OSR exit #", m_ftlState.jitCode->osrExitDescriptors.size(), " with availability: ", availabilityMap(), "\n");
if (!m_availableRecoveries.isEmpty())
@@ -14179,12 +14243,17 @@
return;
blessSpeculation(
- m_out.speculate(failCondition), kind, lowValue, highValue, origin);
+ m_out.speculate(failCondition), kind, lowValue, profile, origin);
}
void blessSpeculation(CheckValue* value, ExitKind kind, FormattedValue lowValue, Node* highValue, NodeOrigin origin)
{
- OSRExitDescriptor* exitDescriptor = appendOSRExitDescriptor(lowValue, highValue);
+ blessSpeculation(value, kind, lowValue, m_graph.methodOfGettingAValueProfileFor(m_node, highValue), origin);
+ }
+
+ void blessSpeculation(CheckValue* value, ExitKind kind, FormattedValue lowValue, const MethodOfGettingAValueProfile& profile, NodeOrigin origin)
+ {
+ OSRExitDescriptor* exitDescriptor = appendOSRExitDescriptor(lowValue, profile);
value->appendColdAnys(buildExitArguments(exitDescriptor, origin.forExit, lowValue));
@@ -14671,7 +14740,6 @@
Output m_out;
Procedure& m_proc;
- LBasicBlock m_prologue;
LBasicBlock m_handleExceptions;
HashMap<DFG::BasicBlock*, LBasicBlock> m_blocks;
@@ -14688,11 +14756,6 @@
HashMap<Node*, LoweredNodeValue> m_storageValues;
HashMap<Node*, LoweredNodeValue> m_doubleValues;
- // This is a bit of a hack. It prevents B3 from having to do CSE on loading of arguments.
- // It's nice to have these optimizations on our end because we can guarantee them a bit better.
- // Probably also saves B3 compile time.
- HashMap<Node*, LValue> m_loadedArgumentValues;
-
HashMap<Node*, LValue> m_phis;
LocalOSRAvailabilityCalculator m_availabilityCalculator;
Modified: trunk/Source/_javascript_Core/ftl/FTLOutput.cpp (221601 => 221602)
--- trunk/Source/_javascript_Core/ftl/FTLOutput.cpp 2017-09-05 03:03:03 UTC (rev 221601)
+++ trunk/Source/_javascript_Core/ftl/FTLOutput.cpp 2017-09-05 03:21:33 UTC (rev 221602)
@@ -861,6 +861,14 @@
value.value()->as<B3::UpsilonValue>()->setPhi(phi);
}
+void Output::entrySwitch(const Vector<LBasicBlock>& cases)
+{
+ RELEASE_ASSERT(cases.size() == m_proc.numEntrypoints());
+ m_block->appendNew<Value>(m_proc, EntrySwitch, origin());
+ for (LBasicBlock block : cases)
+ m_block->appendSuccessor(FrequentedBlock(block));
+}
+
} } // namespace JSC::FTL
#endif // ENABLE(FTL_JIT)
Modified: trunk/Source/_javascript_Core/ftl/FTLOutput.h (221601 => 221602)
--- trunk/Source/_javascript_Core/ftl/FTLOutput.h 2017-09-05 03:03:03 UTC (rev 221601)
+++ trunk/Source/_javascript_Core/ftl/FTLOutput.h 2017-09-05 03:21:33 UTC (rev 221602)
@@ -429,6 +429,8 @@
}
}
+ void entrySwitch(const Vector<LBasicBlock>&);
+
void ret(LValue);
void unreachable();
Modified: trunk/Source/_javascript_Core/jit/JITOperations.cpp (221601 => 221602)
--- trunk/Source/_javascript_Core/jit/JITOperations.cpp 2017-09-05 03:03:03 UTC (rev 221601)
+++ trunk/Source/_javascript_Core/jit/JITOperations.cpp 2017-09-05 03:21:33 UTC (rev 221602)
@@ -42,6 +42,7 @@
#include "ErrorHandlingScope.h"
#include "EvalCodeBlock.h"
#include "ExceptionFuzz.h"
+#include "FTLOSREntry.h"
#include "FrameTracers.h"
#include "FunctionCodeBlock.h"
#include "GetterSetter.h"
@@ -1533,10 +1534,14 @@
NativeCallFrameTracer tracer(&vm, exec);
CodeBlock* optimizedReplacement = exec->codeBlock()->replacement();
- if (optimizedReplacement->jitType() != JITCode::DFGJIT)
- return nullptr;
-
- return static_cast<char*>(DFG::prepareCatchOSREntry(exec, optimizedReplacement, bytecodeIndex));
+ switch (optimizedReplacement->jitType()) {
+ case JITCode::DFGJIT:
+ case JITCode::FTLJIT:
+ return static_cast<char*>(DFG::prepareCatchOSREntry(exec, optimizedReplacement, bytecodeIndex));
+ default:
+ break;
+ }
+ return nullptr;
}
char* JIT_OPERATION operationTryOSREnterAtCatchAndValueProfile(ExecState* exec, uint32_t bytecodeIndex)
@@ -1546,8 +1551,14 @@
CodeBlock* codeBlock = exec->codeBlock();
CodeBlock* optimizedReplacement = codeBlock->replacement();
- if (optimizedReplacement->jitType() == JITCode::DFGJIT)
+
+ switch (optimizedReplacement->jitType()) {
+ case JITCode::DFGJIT:
+ case JITCode::FTLJIT:
return static_cast<char*>(DFG::prepareCatchOSREntry(exec, optimizedReplacement, bytecodeIndex));
+ default:
+ break;
+ }
codeBlock->ensureCatchLivenessIsComputedForBytecodeOffset(bytecodeIndex);
ValueProfileAndOperandBuffer* buffer = static_cast<ValueProfileAndOperandBuffer*>(codeBlock->instructions()[bytecodeIndex + 3].u.pointer);