dfg/DFGPredictionPropagationPhase.cpp
dfg/DFGPromotedHeapLocation.cpp
dfg/DFGPureValue.cpp
- dfg/DFGPutLocalSinkingPhase.cpp
+ dfg/DFGPutStackSinkingPhase.cpp
dfg/DFGResurrectionForValidationPhase.cpp
dfg/DFGSSACalculator.cpp
dfg/DFGSSAConversionPhase.cpp
+2015-02-25 Filip Pizlo <fpizlo@apple.com>
+
+ DFG SSA stack accesses shouldn't speak of VariableAccessDatas
+ https://bugs.webkit.org/show_bug.cgi?id=142036
+
+ Reviewed by Michael Saboff.
+
+ VariableAccessData is a useful thing in LoadStore and ThreadedCPS, but it's purely harmful in
+ SSA because you can't cook up new VariableAccessDatas. So, if you know that you want to load
+ or store to the stack, and you know what format to use as well as the location, then prior to
+ this patch you couldn't do it unless you found some existing VariableAccessData that matched
+ your requirements. That can be a hard task.
+
+ It's better if SSA doesn't speak of VariableAccessDatas but instead just has stack accesses
+ that speak of the things that a stack access needs: local, machineLocal, and format. This
+ patch changes the SSA way of accessing the stack to do just that.
+
+ Also add more IR validation.
+
+ * CMakeLists.txt:
+ * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj:
+ * JavaScriptCore.xcodeproj/project.pbxproj:
+ * dfg/DFGAbstractInterpreterInlines.h:
+ (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+ * dfg/DFGClobberize.h:
+ (JSC::DFG::clobberize):
+ * dfg/DFGConstantFoldingPhase.cpp:
+ (JSC::DFG::ConstantFoldingPhase::foldConstants):
+ * dfg/DFGDoesGC.cpp:
+ (JSC::DFG::doesGC):
+ * dfg/DFGFixupPhase.cpp:
+ (JSC::DFG::FixupPhase::fixupNode):
+ * dfg/DFGFlushFormat.h:
+ (JSC::DFG::isConcrete):
+ * dfg/DFGGraph.cpp:
+ (JSC::DFG::Graph::dump):
+ * dfg/DFGGraph.h:
+ * dfg/DFGMayExit.cpp:
+ (JSC::DFG::mayExit):
+ * dfg/DFGNode.cpp:
+ (JSC::DFG::Node::hasVariableAccessData):
+ * dfg/DFGNode.h:
+ (JSC::DFG::StackAccessData::StackAccessData):
+ (JSC::DFG::StackAccessData::flushedAt):
+ (JSC::DFG::Node::convertToPutStack):
+ (JSC::DFG::Node::convertToGetStack):
+ (JSC::DFG::Node::hasUnlinkedLocal):
+ (JSC::DFG::Node::hasStackAccessData):
+ (JSC::DFG::Node::stackAccessData):
+ (JSC::DFG::Node::willHaveCodeGenOrOSR):
+ * dfg/DFGNodeType.h:
+ * dfg/DFGOSRAvailabilityAnalysisPhase.cpp:
+ (JSC::DFG::LocalOSRAvailabilityCalculator::executeNode):
+ * dfg/DFGPlan.cpp:
+ (JSC::DFG::Plan::compileInThreadImpl):
+ * dfg/DFGPredictionPropagationPhase.cpp:
+ (JSC::DFG::PredictionPropagationPhase::propagate):
+ * dfg/DFGPutLocalSinkingPhase.cpp: Removed.
+ * dfg/DFGPutLocalSinkingPhase.h: Removed.
+ * dfg/DFGPutStackSinkingPhase.cpp: Copied from Source/JavaScriptCore/dfg/DFGPutLocalSinkingPhase.cpp.
+ (JSC::DFG::performPutStackSinking):
+ (JSC::DFG::performPutLocalSinking): Deleted.
+ * dfg/DFGPutStackSinkingPhase.h: Copied from Source/JavaScriptCore/dfg/DFGPutLocalSinkingPhase.h.
+ * dfg/DFGSSAConversionPhase.cpp:
+ (JSC::DFG::SSAConversionPhase::run):
+ * dfg/DFGSafeToExecute.h:
+ (JSC::DFG::safeToExecute):
+ * dfg/DFGSpeculativeJIT32_64.cpp:
+ (JSC::DFG::SpeculativeJIT::compile):
+ * dfg/DFGSpeculativeJIT64.cpp:
+ (JSC::DFG::SpeculativeJIT::compile):
+ * dfg/DFGStackLayoutPhase.cpp:
+ (JSC::DFG::StackLayoutPhase::run):
+ * dfg/DFGValidate.cpp:
+ (JSC::DFG::Validate::validate):
+ (JSC::DFG::Validate::validateCPS):
+ (JSC::DFG::Validate::validateSSA):
+ * dfg/DFGVirtualRegisterAllocationPhase.cpp:
+ (JSC::DFG::VirtualRegisterAllocationPhase::run):
+ * ftl/FTLCapabilities.cpp:
+ (JSC::FTL::canCompile):
+ * ftl/FTLLowerDFGToLLVM.cpp:
+ (JSC::FTL::LowerDFGToLLVM::lower):
+ (JSC::FTL::LowerDFGToLLVM::compileNode):
+ (JSC::FTL::LowerDFGToLLVM::compileGetStack):
+ (JSC::FTL::LowerDFGToLLVM::compilePutStack):
+ (JSC::FTL::LowerDFGToLLVM::compileGetLocal): Deleted.
+ (JSC::FTL::LowerDFGToLLVM::compilePutLocal): Deleted.
+ * ftl/FTLOSRExit.h:
+ * tests/stress/many-sunken-locals.js: Added. This failure mode was caught by some miscellaneous test, so I figured I should write an explicit test for it.
+ (foo):
+ (bar):
+ (baz):
+ (fuzz):
+ (buzz):
+
2015-02-26 Mark Lam <mark.lam@apple.com>
Rolling out r180602, r180608, r180613, r180617, r180671.
<ClCompile Include="..\dfg\DFGPredictionPropagationPhase.cpp" />
<ClCompile Include="..\dfg\DFGPromotedHeapLocation.cpp" />
<ClCompile Include="..\dfg\DFGPureValue.cpp" />
- <ClCompile Include="..\dfg\DFGPutLocalSinkingPhase.cpp" />
+ <ClCompile Include="..\dfg\DFGPutStackSinkingPhase.cpp" />
<ClCompile Include="..\dfg\DFGResurrectionForValidationPhase.cpp" />
<ClCompile Include="..\dfg\DFGSafepoint.cpp" />
<ClCompile Include="..\dfg\DFGSpeculativeJIT.cpp" />
<ClInclude Include="..\dfg\DFGPromoteHeapAccess.h" />
<ClInclude Include="..\dfg\DFGPromotedHeapLocation.h" />
<ClInclude Include="..\dfg\DFGPureValue.h" />
- <ClInclude Include="..\dfg\DFGPutLocalSinkingPhase.h" />
+ <ClInclude Include="..\dfg\DFGPutStackSinkingPhase.h" />
<ClInclude Include="..\dfg\DFGRegisterBank.h" />
<ClInclude Include="..\dfg\DFGRegisterSet.h" />
<ClInclude Include="..\dfg\DFGResurrectionForValidationPhase.h" />
0F38B01817CFE75500B144D3 /* DFGCompilationKey.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F38B01417CFE75500B144D3 /* DFGCompilationKey.h */; settings = {ATTRIBUTES = (Private, ); }; };
0F38B01917CFE75500B144D3 /* DFGCompilationMode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F38B01517CFE75500B144D3 /* DFGCompilationMode.cpp */; };
0F38B01A17CFE75500B144D3 /* DFGCompilationMode.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F38B01617CFE75500B144D3 /* DFGCompilationMode.h */; settings = {ATTRIBUTES = (Private, ); }; };
+ 0F3A1BF91A9ECB7D000DE01A /* DFGPutStackSinkingPhase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F3A1BF71A9ECB7D000DE01A /* DFGPutStackSinkingPhase.cpp */; };
+ 0F3A1BFA1A9ECB7D000DE01A /* DFGPutStackSinkingPhase.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F3A1BF81A9ECB7D000DE01A /* DFGPutStackSinkingPhase.h */; settings = {ATTRIBUTES = (Private, ); }; };
0F3AC752183EA1040032029F /* StackAlignment.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F3AC751183EA1040032029F /* StackAlignment.h */; settings = {ATTRIBUTES = (Private, ); }; };
0F3AC754188E5EC80032029F /* ExitingJITType.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F3AC753188E5EC80032029F /* ExitingJITType.h */; settings = {ATTRIBUTES = (Private, ); }; };
0F3B3A1A153E68F2003ED0FF /* DFGConstantFoldingPhase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F3B3A17153E68EF003ED0FF /* DFGConstantFoldingPhase.cpp */; };
C4F4B6F51A05C984005CAB76 /* generate_objc_protocol_types_implementation.py in Headers */ = {isa = PBXBuildFile; fileRef = C4F4B6D71A05C76F005CAB76 /* generate_objc_protocol_types_implementation.py */; settings = {ATTRIBUTES = (Private, ); }; };
C4F4B6F61A05C984005CAB76 /* objc_generator_templates.py in Headers */ = {isa = PBXBuildFile; fileRef = C4F4B6D81A05C76F005CAB76 /* objc_generator_templates.py */; settings = {ATTRIBUTES = (Private, ); }; };
DC00039319D8BE6F00023EB0 /* DFGPreciseLocalClobberize.h in Headers */ = {isa = PBXBuildFile; fileRef = DC00039019D8BE6F00023EB0 /* DFGPreciseLocalClobberize.h */; settings = {ATTRIBUTES = (Private, ); }; };
- DC00039819DBA70600023EB0 /* DFGPutLocalSinkingPhase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DC00039619DBA70600023EB0 /* DFGPutLocalSinkingPhase.cpp */; };
- DC00039919DBA70600023EB0 /* DFGPutLocalSinkingPhase.h in Headers */ = {isa = PBXBuildFile; fileRef = DC00039719DBA70600023EB0 /* DFGPutLocalSinkingPhase.h */; settings = {ATTRIBUTES = (Private, ); }; };
E124A8F70E555775003091F1 /* OpaqueJSString.h in Headers */ = {isa = PBXBuildFile; fileRef = E124A8F50E555775003091F1 /* OpaqueJSString.h */; settings = {ATTRIBUTES = (Private, ); }; };
E124A8F80E555775003091F1 /* OpaqueJSString.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E124A8F60E555775003091F1 /* OpaqueJSString.cpp */; };
E18E3A590DF9278C00D90B34 /* VM.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E18E3A570DF9278C00D90B34 /* VM.cpp */; };
0F38B01417CFE75500B144D3 /* DFGCompilationKey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGCompilationKey.h; path = dfg/DFGCompilationKey.h; sourceTree = "<group>"; };
0F38B01517CFE75500B144D3 /* DFGCompilationMode.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DFGCompilationMode.cpp; path = dfg/DFGCompilationMode.cpp; sourceTree = "<group>"; };
0F38B01617CFE75500B144D3 /* DFGCompilationMode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGCompilationMode.h; path = dfg/DFGCompilationMode.h; sourceTree = "<group>"; };
+ 0F3A1BF71A9ECB7D000DE01A /* DFGPutStackSinkingPhase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DFGPutStackSinkingPhase.cpp; path = dfg/DFGPutStackSinkingPhase.cpp; sourceTree = "<group>"; };
+ 0F3A1BF81A9ECB7D000DE01A /* DFGPutStackSinkingPhase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGPutStackSinkingPhase.h; path = dfg/DFGPutStackSinkingPhase.h; sourceTree = "<group>"; };
0F3AC751183EA1040032029F /* StackAlignment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StackAlignment.h; sourceTree = "<group>"; };
0F3AC753188E5EC80032029F /* ExitingJITType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ExitingJITType.h; sourceTree = "<group>"; };
0F3B3A17153E68EF003ED0FF /* DFGConstantFoldingPhase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DFGConstantFoldingPhase.cpp; path = dfg/DFGConstantFoldingPhase.cpp; sourceTree = "<group>"; };
D21202280AD4310C00ED79B6 /* DateConversion.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = DateConversion.cpp; sourceTree = "<group>"; };
D21202290AD4310C00ED79B6 /* DateConversion.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = DateConversion.h; sourceTree = "<group>"; };
DC00039019D8BE6F00023EB0 /* DFGPreciseLocalClobberize.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGPreciseLocalClobberize.h; path = dfg/DFGPreciseLocalClobberize.h; sourceTree = "<group>"; };
- DC00039619DBA70600023EB0 /* DFGPutLocalSinkingPhase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DFGPutLocalSinkingPhase.cpp; path = dfg/DFGPutLocalSinkingPhase.cpp; sourceTree = "<group>"; };
- DC00039719DBA70600023EB0 /* DFGPutLocalSinkingPhase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGPutLocalSinkingPhase.h; path = dfg/DFGPutLocalSinkingPhase.h; sourceTree = "<group>"; };
E124A8F50E555775003091F1 /* OpaqueJSString.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OpaqueJSString.h; sourceTree = "<group>"; };
E124A8F60E555775003091F1 /* OpaqueJSString.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OpaqueJSString.cpp; sourceTree = "<group>"; };
E178633F0D9BEC0000D74E75 /* InitializeThreading.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InitializeThreading.h; sourceTree = "<group>"; };
0FAA3E0819D0C2CB00FAC9E2 /* DFGPromoteHeapAccess.h */,
0FB1765E196B8F9E0091052A /* DFGPureValue.cpp */,
0FB1765F196B8F9E0091052A /* DFGPureValue.h */,
- DC00039619DBA70600023EB0 /* DFGPutLocalSinkingPhase.cpp */,
- DC00039719DBA70600023EB0 /* DFGPutLocalSinkingPhase.h */,
+ 0F3A1BF71A9ECB7D000DE01A /* DFGPutStackSinkingPhase.cpp */,
+ 0F3A1BF81A9ECB7D000DE01A /* DFGPutStackSinkingPhase.h */,
86EC9DC11328DF82002B2AD7 /* DFGRegisterBank.h */,
0F666ECA1836B37E00D017F1 /* DFGResurrectionForValidationPhase.cpp */,
0F666ECB1836B37E00D017F1 /* DFGResurrectionForValidationPhase.h */,
BC18C4280E16F5CD00B34460 /* JSStringRef.h in Headers */,
BC18C4290E16F5CD00B34460 /* JSStringRefCF.h in Headers */,
1A28D4A8177B71C80007FA3C /* JSStringRefPrivate.h in Headers */,
- DC00039919DBA70600023EB0 /* DFGPutLocalSinkingPhase.h in Headers */,
0F919D0D157EE0A2004A4E7D /* JSSymbolTableObject.h in Headers */,
BC18C42A0E16F5CD00B34460 /* JSType.h in Headers */,
0F2B66FB17B6B5AB00A7AE3F /* JSTypedArrayConstructors.h in Headers */,
0FC8150A14043BF500CFA603 /* WriteBarrierSupport.h in Headers */,
9688CB160ED12B4E001D649F /* X86Assembler.h in Headers */,
A5840E2A187CA75900843B10 /* xxd.pl in Headers */,
+ 0F3A1BFA1A9ECB7D000DE01A /* DFGPutStackSinkingPhase.h in Headers */,
451539B912DC994500EF7AC4 /* Yarr.h in Headers */,
86704B8512DBA33700A9FE7B /* YarrInterpreter.h in Headers */,
86704B8712DBA33700A9FE7B /* YarrJIT.h in Headers */,
A7D89CF517A0B8CC00773AD8 /* DFGCriticalEdgeBreakingPhase.cpp in Sources */,
0FFFC95914EF90A600C72532 /* DFGCSEPhase.cpp in Sources */,
0F2FC77216E12F710038D976 /* DFGDCEPhase.cpp in Sources */,
- DC00039819DBA70600023EB0 /* DFGPutLocalSinkingPhase.cpp in Sources */,
0F8F2B99172F04FF007DBDA5 /* DFGDesiredIdentifiers.cpp in Sources */,
C2C0F7CD17BBFC5B00464FE4 /* DFGDesiredTransitions.cpp in Sources */,
0FE8534B1723CDA500B618F5 /* DFGDesiredWatchpoints.cpp in Sources */,
0FD120331A8C85BD000F5280 /* FTLJSCallVarargs.cpp in Sources */,
0F9D339617FFC4E60073C2BC /* DFGFlushedAt.cpp in Sources */,
A7D89CF717A0B8CC00773AD8 /* DFGFlushFormat.cpp in Sources */,
+ 0F3A1BF91A9ECB7D000DE01A /* DFGPutStackSinkingPhase.cpp in Sources */,
86EC9DC71328DF82002B2AD7 /* DFGGraph.cpp in Sources */,
0F2FCCF918A60070001A27F8 /* DFGGraphSafepoint.cpp in Sources */,
A704D90517A0BAA8006BA554 /* DFGInPlaceAbstractState.cpp in Sources */,
break;
}
+ case GetStack: {
+ StackAccessData* data = node->stackAccessData();
+ AbstractValue value = m_state.variables().operand(data->local);
+ // The value in the local should already be checked.
+ DFG_ASSERT(m_graph, node, value.isType(typeFilterFor(data->format)));
+ if (value.value())
+ m_state.setFoundConstants(true);
+ forNode(node) = value;
+ break;
+ }
+
case GetLocalUnlinked: {
AbstractValue value = m_state.variables().operand(node->unlinkedLocal().offset());
if (value.value())
break;
}
- case SetLocal:
- case PutLocal: {
- m_state.variables().operand(node->local().offset()) = forNode(node->child1());
+ case SetLocal: {
+ m_state.variables().operand(node->local()) = forNode(node->child1());
+ break;
+ }
+
+ case PutStack: {
+ m_state.variables().operand(node->stackAccessData()->local) = forNode(node->child1());
break;
}
break;
}
- case KillLocal: {
+ case KillStack: {
// This is just a hint telling us that the OSR state of the local is no longer inside the
// flushed data.
break;
case MovHint:
case ZombieHint:
- case KillLocal:
+ case KillStack:
case Upsilon:
case Phi:
case PhantomLocal:
return;
case SetLocal:
- case PutLocal:
write(AbstractHeap(Variables, node->local()));
def(HeapLocation(VariableLoc, AbstractHeap(Variables, node->local())), node->child1().node());
return;
+ case GetStack: {
+ AbstractHeap heap(Variables, node->stackAccessData()->local);
+ read(heap);
+ def(HeapLocation(VariableLoc, heap), node);
+ return;
+ }
+
+ case PutStack: {
+ AbstractHeap heap(Variables, node->stackAccessData()->local);
+ write(heap);
+ def(HeapLocation(VariableLoc, heap), node->child1().node());
+ return;
+ }
+
case LoadVarargs:
// This actually writes to local variables as well. But when it reads the array, it does
// so in a way that may trigger getters or various traps.
m_insertionSet.insertNode(
indexInBlock, SpecNone, Phantom, node->origin, node->children);
- node->convertToGetLocalUnlinked(VirtualRegister(operand));
+ if (m_graph.m_form == SSA)
+ node->convertToGetStack(m_graph.m_stackAccessData.add(VirtualRegister(operand), FlushedJSValue));
+ else
+ node->convertToGetLocalUnlinked(VirtualRegister(operand));
break;
}
}
case PutByOffsetHint:
case CheckStructureImmediate:
case PutStructureHint:
- case PutLocal:
- case KillLocal:
+ case PutStack:
+ case KillStack:
+ case GetStack:
return false;
case CreateActivation:
case CheckStructureImmediate:
case PutStructureHint:
case MaterializeNewObject:
- case PutLocal:
- case KillLocal:
+ case PutStack:
+ case KillStack:
+ case GetStack:
// These are just nodes that we don't currently expect to see during fixup.
// If we ever wanted to insert them prior to fixup, then we just have to create
// fixup rules for them.
return ConflictingFlush;
}
+inline bool isConcrete(FlushFormat format)
+{
+ return format != DeadFlush && format != ConflictingFlush;
+}
+
} } // namespace JSC::DFG
namespace WTF {
out.print(comma, "machine:", operand);
}
}
+ if (node->hasStackAccessData()) {
+ StackAccessData* data = node->stackAccessData();
+ out.print(comma, data->local);
+ if (data->machineLocal.isValid())
+ out.print(comma, "machine:", data->machineLocal);
+ out.print(comma, data->format);
+ }
if (node->hasUnlinkedLocal())
out.print(comma, node->unlinkedLocal());
if (node->hasUnlinkedMachineLocal()) {
// that survived DCE. All of them except maybe "this" will survive DCE, because of the Flush
// nodes.
//
- // In SSA, this is all of the GetLocal nodes for the arguments in the machine code block that
+ // 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:
//
// Assume that x is always int during profiling. The ArithAdd for "x + 1" will be dead and will
// have a proven check for the edge to "x". So, we will not insert a Check node and we will
- // kill the GetLocal for "x". But, we must do the int check in the progolue, because that's the
+ // kill the GetStack for "x". But, we must do the int check in the progolue, because that's the
// thing we used to allow DCE of ArithAdd. Otherwise the add could be impure:
//
// var o = {
Bag<ObjectMaterializationData> m_objectMaterializationData;
Bag<CallVarargsData> m_callVarargsData;
Bag<LoadVarargsData> m_loadVarargsData;
+ Bag<StackAccessData> m_stackAccessData;
Vector<InlineVariableData, 4> m_inlineVariableData;
HashMap<CodeBlock*, std::unique_ptr<FullBytecodeLiveness>> m_bytecodeLiveness;
bool m_hasArguments;
case PutStructureHint:
case PutByOffsetHint:
case PhantomNewObject:
- case PutLocal:
- case KillLocal:
+ case PutStack:
+ case KillStack:
+ case GetStack:
case GetCallee:
case GetScope:
case PhantomLocal:
case SetArgument:
case Flush:
case PhantomLocal:
- case PutLocal:
return true;
default:
return false;
unsigned limit; // Maximum number of elements to load. Includes "this".
};
+struct StackAccessData {
+ StackAccessData()
+ : format(DeadFlush)
+ {
+ }
+
+ StackAccessData(VirtualRegister local, FlushFormat format)
+ : local(local)
+ , format(format)
+ {
+ }
+
+ VirtualRegister local;
+ VirtualRegister machineLocal;
+ FlushFormat format;
+
+ FlushedAt flushedAt() { return FlushedAt(format, machineLocal); }
+};
+
// This type used in passing an immediate argument to Node constructor;
// distinguishes an immediate value (typically an index into a CodeBlock data structure -
// a constant index, argument, or identifier) from a Node*.
children.reset();
}
+ void convertToPutStack(StackAccessData* data)
+ {
+ m_op = PutStack;
+ m_flags |= NodeMustGenerate;
+ m_opInfo = bitwise_cast<uintptr_t>(data);
+ m_opInfo2 = 0;
+ }
+
+ void convertToGetStack(StackAccessData* data)
+ {
+ m_op = GetStack;
+ m_flags &= ~NodeMustGenerate;
+ m_opInfo = bitwise_cast<uintptr_t>(data);
+ m_opInfo2 = 0;
+ children.reset();
+ }
+
void convertToGetByOffset(StorageAccessData& data, Edge storage)
{
ASSERT(m_op == GetById || m_op == GetByIdFlush || m_op == MultiGetByOffset);
case ExtractOSREntryLocal:
case MovHint:
case ZombieHint:
- case KillLocal:
+ case KillStack:
return true;
default:
return false;
return VirtualRegister(m_opInfo2);
}
+ bool hasStackAccessData()
+ {
+ switch (op()) {
+ case PutStack:
+ case GetStack:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ StackAccessData* stackAccessData()
+ {
+ ASSERT(hasStackAccessData());
+ return bitwise_cast<StackAccessData*>(m_opInfo);
+ }
+
bool hasPhi()
{
return op() == Upsilon;
{
switch (op()) {
case SetLocal:
- case PutLocal:
- case KillLocal:
case MovHint:
case ZombieHint:
case PhantomArguments:
/* better names for a lot of these. https://bugs.webkit.org/show_bug.cgi?id=137307 */\
macro(GetLocal, NodeResultJS) \
macro(SetLocal, 0) \
- macro(PutLocal, NodeMustGenerate) \
- macro(KillLocal, NodeMustGenerate) \
+ \
+ macro(PutStack, NodeMustGenerate) \
+ macro(KillStack, NodeMustGenerate) \
+ macro(GetStack, NodeResultJS) \
+ \
macro(MovHint, 0) \
macro(ZombieHint, 0) \
macro(Phantom, NodeMustGenerate) \
void LocalOSRAvailabilityCalculator::executeNode(Node* node)
{
switch (node->op()) {
- case PutLocal: {
- VariableAccessData* variable = node->variableAccessData();
- m_availability.m_locals.operand(variable->local()).setFlush(variable->flushedAt());
+ case PutStack: {
+ StackAccessData* data = node->stackAccessData();
+ m_availability.m_locals.operand(data->local).setFlush(data->flushedAt());
break;
}
- case KillLocal: {
+ case KillStack: {
m_availability.m_locals.operand(node->unlinkedLocal()).setFlush(FlushedAt(ConflictingFlush));
break;
}
- case GetLocal: {
- VariableAccessData* variable = node->variableAccessData();
- m_availability.m_locals.operand(variable->local()) =
- Availability(node, variable->flushedAt());
+ case GetStack: {
+ StackAccessData* data = node->stackAccessData();
+ m_availability.m_locals.operand(data->local) = Availability(node, data->flushedAt());
break;
}
/*
- * Copyright (C) 2013, 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2013-2015 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
#include "DFGPhantomRemovalPhase.h"
#include "DFGPredictionInjectionPhase.h"
#include "DFGPredictionPropagationPhase.h"
-#include "DFGPutLocalSinkingPhase.h"
+#include "DFGPutStackSinkingPhase.h"
#include "DFGResurrectionForValidationPhase.h"
#include "DFGSSAConversionPhase.h"
#include "DFGSSALoweringPhase.h"
performCPSRethreading(dfg);
performSSAConversion(dfg);
performSSALowering(dfg);
- performPutLocalSinking(dfg);
+ performPutStackSinking(dfg);
performGlobalCSE(dfg);
performLivenessAnalysis(dfg);
performCFA(dfg);
case CheckStructureImmediate:
case PutStructureHint:
case MaterializeNewObject:
- case PutLocal:
- case KillLocal: {
+ case PutStack:
+ case KillStack:
+ case GetStack: {
// This node should never be visible at this stage of compilation. It is
// inserted by fixup(), which follows this phase.
RELEASE_ASSERT_NOT_REACHED();
/*
- * Copyright (C) 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2014, 2015 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
*/
#include "config.h"
-#include "DFGPutLocalSinkingPhase.h"
+#include "DFGPutStackSinkingPhase.h"
#if ENABLE(DFG_JIT)
bool verbose = false;
-class VariableDeferral {
+class PutStackSinkingPhase : public Phase {
public:
- VariableDeferral(VariableAccessData* variable = nullptr)
- : m_variable(variable)
- {
- }
-
- static VariableDeferral conflict()
- {
- return VariableDeferral(conflictMarker());
- }
-
- bool operator!() const { return !m_variable; }
-
- bool hasVariable() const { return !!*this && !isConflict(); }
-
- VariableAccessData* variable() const
- {
- ASSERT(hasVariable());
- return m_variable;
- }
-
- bool isConflict() const
- {
- return m_variable == conflictMarker();
- }
-
- VariableDeferral merge(VariableDeferral other) const
- {
- if (*this == other || !other)
- return *this;
- if (!*this)
- return other;
- return conflict();
- }
-
- bool operator==(VariableDeferral other) const
- {
- return m_variable == other.m_variable;
- }
-
- void dump(PrintStream& out) const
- {
- if (!*this)
- out.print("-");
- else if (isConflict())
- out.print("Conflict");
- else
- out.print(RawPointer(m_variable));
- }
-
- void dumpInContext(PrintStream& out, DumpContext*) const
- {
- dump(out);
- }
-
-private:
- static VariableAccessData* conflictMarker()
- {
- return bitwise_cast<VariableAccessData*>(static_cast<intptr_t>(1));
- }
-
- VariableAccessData* m_variable;
-};
-
-class PutLocalSinkingPhase : public Phase {
-public:
- PutLocalSinkingPhase(Graph& graph)
- : Phase(graph, "PutLocal sinking")
+ PutStackSinkingPhase(Graph& graph)
+ : Phase(graph, "PutStack sinking")
{
}
bool run()
{
// FIXME: One of the problems of this approach is that it will create a duplicate Phi graph
- // for sunken PutLocals in the presence of interesting control flow merges, and where the
- // value being PutLocal'd is also otherwise live in the DFG code. We could work around this
+ // for sunken PutStacks in the presence of interesting control flow merges, and where the
+ // value being PutStack'd is also otherwise live in the DFG code. We could work around this
// by doing the sinking over CPS, or maybe just by doing really smart hoisting. It's also
// possible that the duplicate Phi graph can be deduplicated by LLVM. It would be best if we
// could observe that there is already a Phi graph in place that does what we want. In
// principle if we have a request to place a Phi at a particular place, we could just check
- // if there is already a Phi that does what we want. Because PutLocalSinkingPhase runs just
+ // if there is already a Phi that does what we want. Because PutStackSinkingPhase runs just
// after SSA conversion, we have almost a guarantee that the Phi graph we produce here would
// be trivially redundant to the one we already have.
if (verbose) {
- dataLog("Graph before PutLocal sinking:\n");
+ dataLog("Graph before PutStack sinking:\n");
m_graph.dump();
}
return;
}
- RELEASE_ASSERT(node->op() == PutLocal);
+ RELEASE_ASSERT(node->op() == PutStack);
live.operand(operand) = false;
});
}
for (size_t i = liveAtHead.atIndex(0).numberOfArguments(); i--;)
DFG_ASSERT(m_graph, nullptr, liveAtHead.atIndex(0).argument(i));
- // Next identify where we would want to sink PutLocals to. We say that there is a deferred
- // flush if we had a PutLocal with a given VariableAccessData* but it hasn't been
- // materialized yet. Deferrals have the following lattice; but it's worth noting that the
- // TOP part of the lattice serves an entirely different purpose than the rest of the lattice:
- // it just means that we're in a region of code where nobody should have been relying on the
- // value. The rest of the lattice means that we either have a PutLocal that is deferred (i.e.
- // still needs to be executed) or there isn't one (because we've alraedy executed it).
+ // Next identify where we would want to sink PutStacks to. We say that there is a deferred
+ // flush if we had a PutStack with a given FlushFormat but it hasn't been materialized yet.
+ // Deferrals have the following lattice; but it's worth noting that the TOP part of the
+ // lattice serves an entirely different purpose than the rest of the lattice: it just means
+ // that we're in a region of code where nobody should have been relying on the value. The
+ // rest of the lattice means that we either have a PutStack that is deferred (i.e. still
+ // needs to be executed) or there isn't one (because we've alraedy executed it).
//
// Bottom:
- // Instantiated as VariableDeferral().
- // Means that all previous PutLocals have been executed so there is nothing deferred.
+ // Represented as DeadFlush.
+ // Means that all previous PutStacks have been executed so there is nothing deferred.
// During merging this is subordinate to the other kinds of deferrals, because it
- // represents the fact that we've already executed all necessary PutLocals. This implies
- // that there *had* been some PutLocals that we should have executed.
+ // represents the fact that we've already executed all necessary PutStacks. This implies
+ // that there *had* been some PutStacks that we should have executed.
//
// Top:
- // Instantiated as VariableDeferral::conflict().
+ // Represented as ConflictingFlush.
// Represents the fact that we know, via forward flow, that there isn't any value in the
// given local that anyone should have been relying on. This comes into play at the
// prologue (because in SSA form at the prologue no local has any value) or when we merge
- // deferrals for different VariableAccessData*'s. A VAD encompasses a lexical scope in
- // which the local has some semantic meaning; if we had stores from different lexical
- // scopes that got merged together then we know that we're not in either scope anymore.
- // Note that this is all approximate and only precise enough to later answer questions
- // pertinent to sinking. For example, this doesn't always detect when a local is no
- // longer semantically relevant - we may well have a deferral from inside some inlined
- // call survive outside of that inlined code, and this is generally OK. In the worst case
- // it means that we might think that a deferral that is actually dead must still be
- // executed. But we usually catch that with liveness. Liveness doesn't always catch it
- // because liveness is conservative.
+ // deferrals for different formats's. A lexical scope in which a local had some semantic
+ // meaning will by this point share the same format; if we had stores from different
+ // lexical scopes that got merged together then we may have a conflicting format. Hence
+ // a conflicting format proves that we're no longer in an area in which the variable was
+ // in scope. Note that this is all approximate and only precise enough to later answer
+ // questions pertinent to sinking. For example, this doesn't always detect when a local
+ // is no longer semantically relevant - we may well have a deferral from inside some
+ // inlined call survive outside of that inlined code, and this is generally OK. In the
+ // worst case it means that we might think that a deferral that is actually dead must
+ // still be executed. But we usually catch that with liveness. Liveness usually catches
+ // such cases, but that's not guaranteed since liveness is conservative.
//
// What Top does give us is detects situations where we both don't need to care about a
// deferral and there is no way that we could reason about it anyway. If we merged
- // deferrals for different variables then we wouldn't know the format to use. So, we
- // use Top in that case because that's also a case where we know that we can ignore the
+ // deferrals for different formats then we wouldn't know the format to use. So, we use
+ // Top in that case because that's also a case where we know that we can ignore the
// deferral.
//
- // Deferral with a concrete VariableAccessData*:
- // Instantiated as VariableDeferral(someVariableAccessData)
- // Represents the fact that the original code would have done a PutLocal but we haven't
- // identified an operation that would have observed that PutLocal.
+ // Deferral with a concrete format:
+ // Represented by format values other than DeadFlush or ConflictingFlush.
+ // Represents the fact that the original code would have done a PutStack but we haven't
+ // identified an operation that would have observed that PutStack.
//
// This code has some interesting quirks because of the fact that neither liveness nor
// deferrals are very precise. They are only precise enough to be able to correctly tell us
- // when we may [sic] need to execute PutLocals. This means that they may report the need to
- // execute a PutLocal in cases where we actually don't really need it, and that's totally OK.
- BlockMap<Operands<VariableDeferral>> deferredAtHead(m_graph);
- BlockMap<Operands<VariableDeferral>> deferredAtTail(m_graph);
+ // when we may [sic] need to execute PutStacks. This means that they may report the need to
+ // execute a PutStack in cases where we actually don't really need it, and that's totally OK.
+ BlockMap<Operands<FlushFormat>> deferredAtHead(m_graph);
+ BlockMap<Operands<FlushFormat>> deferredAtTail(m_graph);
for (BasicBlock* block : m_graph.blocksInNaturalOrder()) {
deferredAtHead[block] =
- Operands<VariableDeferral>(OperandsLike, block->variablesAtHead);
+ Operands<FlushFormat>(OperandsLike, block->variablesAtHead);
deferredAtTail[block] =
- Operands<VariableDeferral>(OperandsLike, block->variablesAtHead);
+ Operands<FlushFormat>(OperandsLike, block->variablesAtHead);
}
- deferredAtHead.atIndex(0).fill(VariableDeferral::conflict());
+ deferredAtHead.atIndex(0).fill(ConflictingFlush);
do {
changed = false;
for (BasicBlock* block : m_graph.blocksInNaturalOrder()) {
- Operands<VariableDeferral> deferred = deferredAtHead[block];
+ Operands<FlushFormat> deferred = deferredAtHead[block];
for (Node* node : *block) {
if (verbose)
dataLog("Deferred at ", node, ":", deferred, "\n");
- if (node->op() == KillLocal) {
- deferred.operand(node->unlinkedLocal()) = VariableDeferral::conflict();
+ if (node->op() == KillStack) {
+ deferred.operand(node->unlinkedLocal()) = ConflictingFlush;
continue;
}
if (operand.isHeader())
return;
// We will materialize just before any reads.
- deferred.operand(operand) = VariableDeferral();
+ deferred.operand(operand) = DeadFlush;
};
preciseLocalClobberize(
return;
}
- deferred.operand(operand) = VariableDeferral(node->variableAccessData());
+ deferred.operand(operand) = node->stackAccessData()->format;
});
}
dataLog("Considering ", VirtualRegister(deferred.operandForIndex(i)), " at ", pointerDump(block), "->", pointerDump(successor), ": ", deferred[i], " and ", deferredAtHead[successor][i], " merges to ");
deferredAtHead[successor][i] =
- deferredAtHead[successor][i].merge(deferred[i]);
+ merge(deferredAtHead[successor][i], deferred[i]);
if (verbose)
dataLog(deferredAtHead[successor][i], "\n");
} while (changed);
- // We wish to insert PutLocals at all of the materialization points, which are defined
+ // We wish to insert PutStacks at all of the materialization points, which are defined
// implicitly as the places where we set deferred to Dead while it was previously not Dead.
// To do this, we may need to build some Phi functions to handle stuff like this:
//
// Before:
//
// if (p)
- // PutLocal(r42, @x)
+ // PutStack(r42, @x)
// else
- // PutLocal(r42, @y)
+ // PutStack(r42, @y)
//
// After:
//
// else
// Upsilon(@y, ^z)
// z: Phi()
- // PutLocal(r42, @z)
+ // PutStack(r42, @z)
//
// This means that we have an SSACalculator::Variable for each local, and a Def is any
- // PutLocal in the original program. The original PutLocals will simply vanish.
+ // PutStack in the original program. The original PutStacks will simply vanish.
Operands<SSACalculator::Variable*> operandToVariable(
OperandsLike, m_graph.block(0)->variablesAtHead);
for (BasicBlock* block : m_graph.blocksInNaturalOrder()) {
for (Node* node : *block) {
switch (node->op()) {
- case PutLocal:
+ case PutStack:
putLocalsToSink.add(node);
ssaCalculator.newDef(
- operandToVariable.operand(node->local()), block, node->child1().node());
+ operandToVariable.operand(node->stackAccessData()->local),
+ block, node->child1().node());
break;
- case GetLocal:
+ case GetStack:
ssaCalculator.newDef(
- operandToVariable.operand(node->local()), block, node);
+ operandToVariable.operand(node->stackAccessData()->local),
+ block, node);
break;
default:
break;
if (!liveAtHead[block].operand(operand))
return nullptr;
- VariableDeferral variableDeferral = deferredAtHead[block].operand(operand);
+ FlushFormat format = deferredAtHead[block].operand(operand);
// We could have an invalid deferral because liveness is imprecise.
- if (!variableDeferral.hasVariable())
+ if (!isConcrete(format))
return nullptr;
if (verbose)
dataLog("Adding Phi for ", operand, " at ", pointerDump(block), "\n");
Node* phiNode = m_graph.addNode(SpecHeapTop, Phi, NodeOrigin());
- DFG_ASSERT(m_graph, nullptr, variableDeferral.hasVariable());
- FlushFormat format = variableDeferral.variable()->flushFormat();
phiNode->mergeFlags(resultFor(format));
return phiNode;
});
Operands<Node*> mapping(OperandsLike, m_graph.block(0)->variablesAtHead);
- Operands<VariableDeferral> deferred;
+ Operands<FlushFormat> deferred;
for (BasicBlock* block : m_graph.blocksInNaturalOrder()) {
mapping.fill(nullptr);
dataLog("Deferred at ", node, ":", deferred, "\n");
switch (node->op()) {
- case PutLocal: {
- VariableAccessData* variable = node->variableAccessData();
- VirtualRegister operand = variable->local();
- deferred.operand(operand) = VariableDeferral(variable);
+ case PutStack: {
+ StackAccessData* data = node->stackAccessData();
+ VirtualRegister operand = data->local;
+ deferred.operand(operand) = data->format;
if (verbose)
dataLog(" Mapping ", operand, " to ", node->child1().node(), " at ", node, "\n");
mapping.operand(operand) = node->child1().node();
break;
}
- case KillLocal: {
- deferred.operand(node->unlinkedLocal()) = VariableDeferral();
+ case KillStack: {
+ deferred.operand(node->unlinkedLocal()) = ConflictingFlush;
break;
}
if (operand.isHeader())
return;
- VariableDeferral variableDeferral = deferred.operand(operand);
- if (!variableDeferral.hasVariable())
+ FlushFormat format = deferred.operand(operand);
+ if (!isConcrete(format))
return;
- // Gotta insert a PutLocal.
+ // Gotta insert a PutStack.
if (verbose)
- dataLog("Inserting a PutLocal for ", operand, " at ", node, "\n");
+ dataLog("Inserting a PutStack for ", operand, " at ", node, "\n");
Node* incoming = mapping.operand(operand);
DFG_ASSERT(m_graph, node, incoming);
insertionSet.insertNode(
- nodeIndex, SpecNone, PutLocal, node->origin,
- OpInfo(variableDeferral.variable()),
- Edge(incoming, useKindFor(variableDeferral.variable()->flushFormat())));
+ nodeIndex, SpecNone, PutStack, node->origin,
+ OpInfo(m_graph.m_stackAccessData.add(operand, format)),
+ Edge(incoming, useKindFor(format)));
- deferred.operand(operand) = nullptr;
+ deferred.operand(operand) = DeadFlush;
};
preciseLocalClobberize(
m_graph, node, escapeHandler, escapeHandler,
[&] (VirtualRegister, Node*) { });
- // If we're a GetLocal, then we also create a mapping.
+ // If we're a GetStack, then we also create a mapping.
// FIXME: We should be able to just eliminate such GetLocals, when we know
// what their incoming value will be.
// https://bugs.webkit.org/show_bug.cgi?id=141624
- if (node->op() == GetLocal) {
- VariableAccessData* variable = node->variableAccessData();
- VirtualRegister operand = variable->local();
+ if (node->op() == GetStack) {
+ StackAccessData* data = node->stackAccessData();
+ VirtualRegister operand = data->local;
mapping.operand(operand) = node;
}
break;
VirtualRegister operand = indexToOperand[variable->index()];
if (verbose)
dataLog("Creating Upsilon for ", operand, " at ", pointerDump(block), "->", pointerDump(successorBlock), "\n");
- VariableDeferral variableDeferral =
- deferredAtHead[successorBlock].operand(operand);
- DFG_ASSERT(m_graph, nullptr, variableDeferral.hasVariable());
- FlushFormat format = variableDeferral.variable()->flushFormat();
+ FlushFormat format = deferredAtHead[successorBlock].operand(operand);
+ DFG_ASSERT(m_graph, nullptr, isConcrete(format));
UseKind useKind = useKindFor(format);
Node* incoming = mapping.operand(operand);
if (!incoming) {
// if (predicate1)
// PutClosureVar(loc42) // prevent GCSE of our GetClosureVar's
// if (predicate2)
- // PutLocal(loc42) // we now have a concrete deferral
+ // PutStack(loc42) // we now have a concrete deferral
// // we still have the concrete deferral because we merged with bottom
// GetClosureVar(loc42) // force materialization
//
// We will have a Phi with no incoming value form the basic block that
- // bypassed the PutLocal.
+ // bypassed the PutStack.
// Note: we sort of could have used the equivalent of LLVM's undef here. The
// point is that it's OK to just leave random bits in the local if we're
insertionSet.execute(block);
}
- // Finally eliminate the sunken PutLocals by turning them into Phantoms. This keeps whatever
+ // Finally eliminate the sunken PutStacks by turning them into Phantoms. This keeps whatever
// type check they were doing. Also prepend KillLocals to them to ensure that we know that
// the relevant value was *not* stored to the stack.
for (BasicBlock* block : m_graph.blocksInNaturalOrder()) {
continue;
insertionSet.insertNode(
- nodeIndex, SpecNone, KillLocal, node->origin, OpInfo(node->local().offset()));
+ nodeIndex, SpecNone, KillStack, node->origin, OpInfo(node->stackAccessData()->local.offset()));
node->convertToPhantom();
}
}
if (verbose) {
- dataLog("Graph after PutLocal sinking:\n");
+ dataLog("Graph after PutStack sinking:\n");
m_graph.dump();
}
} // anonymous namespace
-bool performPutLocalSinking(Graph& graph)
+bool performPutStackSinking(Graph& graph)
{
- SamplingRegion samplingRegion("DFG PutLocal Sinking Phase");
- return runPhase<PutLocalSinkingPhase>(graph);
+ SamplingRegion samplingRegion("DFG PutStack Sinking Phase");
+ return runPhase<PutStackSinkingPhase>(graph);
}
} } // namespace JSC::DFG
/*
- * Copyright (C) 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2014, 2015 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef DFGPutLocalSinkingPhase_h
-#define DFGPutLocalSinkingPhase_h
+#ifndef DFGPutStackSinkingPhase_h
+#define DFGPutStackSinkingPhase_h
#if ENABLE(DFG_JIT)
class Graph;
-// Sinks PutLocals to the absolute latest point where they can possibly happen, which is usually
-// side-effects that may observe them. This eliminates PutLocals if it sinks them past the point of
+// Sinks PutStacks to the absolute latest point where they can possibly happen, which is usually
+// side-effects that may observe them. This eliminates PutStacks if it sinks them past the point of
// their deaths.
-bool performPutLocalSinking(Graph&);
+bool performPutStackSinking(Graph&);
} } // namespace JSC::DFG
#endif // ENABLE(DFG_JIT)
-#endif // DFGPutLocalSinkingPhase_h
+#endif // DFGPutStackSinkingPhase_h
ASSERT(node->op() == SetArgument);
childNode = m_insertionSet.insertNode(
nodeIndex, node->variableAccessData()->prediction(),
- GetLocal, node->origin, OpInfo(node->variableAccessData()));
- m_argumentGetters.add(childNode);
+ GetStack, node->origin,
+ OpInfo(m_graph.m_stackAccessData.add(variable->local(), variable->flushFormat())));
+ if (!ASSERT_DISABLED)
+ m_argumentGetters.add(childNode);
m_argumentMapping.add(node, childNode);
}
//
// - MovHint has KillLocal prepended to it.
//
- // - GetLocal over captured variables lose their phis.
+ // - GetLocal over captured variables lose their phis and become GetStack.
//
// - GetLocal over uncaptured variables die and get replaced with references to the node
// specified by valueForOperand.
//
- // - SetLocal turns into PutLocal if it's flushed, or turns into a Check otherwise.
+ // - SetLocal turns into PutStack if it's flushed, or turns into a Check otherwise.
//
// - Flush loses its children and turns into a Phantom.
//
// - PhantomLocal becomes Phantom, and its child is whatever is specified by
// valueForOperand.
//
- // - SetArgument is removed. Note that GetLocal nodes have already been inserted.
+ // - SetArgument is removed. Note that GetStack nodes have already been inserted.
Operands<Node*> valueForOperand(OperandsLike, m_graph.block(0)->variablesAtHead);
for (BasicBlock* block : m_graph.blocksInPreOrder()) {
valueForOperand.clear();
// CPS will claim that the root block has all arguments live. But we have already done
// the first step of SSA conversion: argument locals are no longer live at head;
- // instead we have GetLocal nodes for extracting the values of arguments. So, we
+ // instead we have GetStack nodes for extracting the values of arguments. So, we
// skip the at-head available value calculation for the root block.
if (block != m_graph.block(0)) {
for (size_t i = valueForOperand.size(); i--;) {
switch (node->op()) {
case MovHint: {
m_insertionSet.insertNode(
- nodeIndex, SpecNone, KillLocal, node->origin,
+ nodeIndex, SpecNone, KillStack, node->origin,
OpInfo(node->unlinkedLocal().offset()));
break;
}
case SetLocal: {
VariableAccessData* variable = node->variableAccessData();
- if (variable->isCaptured() || !!(node->flags() & NodeIsFlushed))
- node->setOpAndDefaultFlags(PutLocal);
- else
+ if (variable->isCaptured() || !!(node->flags() & NodeIsFlushed)) {
+ node->convertToPutStack(
+ m_graph.m_stackAccessData.add(
+ variable->local(), variable->flushFormat()));
+ } else
node->setOpAndDefaultFlags(Check);
if (!variable->isCaptured()) {
break;
}
+ case GetStack: {
+ ASSERT(m_argumentGetters.contains(node));
+ valueForOperand.operand(node->stackAccessData()->local) = node;
+ break;
+ }
+
case GetLocal: {
VariableAccessData* variable = node->variableAccessData();
- if (m_argumentGetters.contains(node)) {
- if (verbose)
- dataLog("Mapping: ", variable->local(), " -> ", node, "\n");
- valueForOperand.operand(variable->local()) = node;
- break;
- }
node->children.reset();
- if (variable->isCaptured())
+ if (variable->isCaptured()) {
+ node->convertToGetStack(m_graph.m_stackAccessData.add(variable->local(), variable->flushFormat()));
break;
+ }
node->convertToPhantom();
if (verbose)
// track the argument loads of those arguments for which we speculate type. We don't
// speculate type for captured arguments.
if (node)
- format = node->variableAccessData()->flushFormat();
+ format = node->stackAccessData()->format;
m_graph.m_argumentFormats[i] = format;
m_graph.m_arguments[i] = node; // Record the load that loads the arguments for the benefit of exit profiling.
case GetCallee:
case GetLocal:
case SetLocal:
- case PutLocal:
- case KillLocal:
+ case PutStack:
+ case KillStack:
+ case GetStack:
case MovHint:
case ZombieHint:
case Phantom:
case CheckStructureImmediate:
case PutStructureHint:
case MaterializeNewObject:
- case PutLocal:
- case KillLocal:
+ case PutStack:
+ case KillStack:
+ case GetStack:
RELEASE_ASSERT_NOT_REACHED();
break;
}
case CheckStructureImmediate:
case PutStructureHint:
case MaterializeNewObject:
- case PutLocal:
- case KillLocal:
+ case PutStack:
+ case KillStack:
+ case GetStack:
DFG_CRASH(m_jit.graph(), node, "Unexpected node");
break;
}
switch (node->op()) {
case GetLocal:
case SetLocal:
- case PutLocal:
case Flush:
case PhantomLocal: {
VariableAccessData* variable = node->variableAccessData();
break;
}
+ case PutStack:
+ case GetStack: {
+ StackAccessData* stack = node->stackAccessData();
+ if (stack->local.isArgument())
+ break;
+ usedLocals.set(stack->local.toLocal());
+ break;
+ }
+
default:
break;
}
variable->machineLocal() = assign(allocation, variable->local());
}
+ for (StackAccessData* data : m_graph.m_stackAccessData) {
+ if (!data->local.isLocal()) {
+ data->machineLocal = data->local;
+ continue;
+ }
+
+ if (static_cast<size_t>(data->local.toLocal()) >= allocation.size())
+ continue;
+ if (allocation[data->local.toLocal()] == UINT_MAX)
+ continue;
+
+ data->machineLocal = assign(allocation, data->local);
+ }
+
if (codeBlock()->usesArguments()) {
VirtualRegister argumentsRegister =
assign(allocation, codeBlock()->argumentsRegister());
VALIDATE((node), canonicalResultRepresentation(node->result()) == canonicalResultRepresentation(node->child1()->result()));
break;
case SetLocal:
- case PutLocal:
+ case PutStack:
case Upsilon:
VALIDATE((node), !!node->child1());
switch (node->child1().useKind()) {
}
}
+ switch (node->op()) {
+ case Phi:
+ case Upsilon:
+ case CheckInBounds:
+ case PhantomNewObject:
+ case PutByOffsetHint:
+ case CheckStructureImmediate:
+ case PutStructureHint:
+ case MaterializeNewObject:
+ case PutStack:
+ case KillStack:
+ case GetStack:
+ VALIDATE((node), !"unexpected node type in CPS");
+ break;
+ default:
+ break;
+ }
+
if (!node->shouldGenerate())
continue;
switch (node->op()) {
if (!block)
continue;
+ VALIDATE((block), block->phis.isEmpty());
+
unsigned nodeIndex = 0;
for (; nodeIndex < block->size() && !block->at(nodeIndex)->origin.forExit.isSet(); nodeIndex++) { }
VALIDATE((node), !node->origin.forExit.isSet());
break;
+ case GetLocal:
+ case SetLocal:
+ case GetLocalUnlinked:
+ case SetArgument:
+ VALIDATE((node), !"bad node type for SSA");
+ break;
+
default:
// FIXME: Add more things here.
// https://bugs.webkit.org/show_bug.cgi?id=123471
bool run()
{
+ DFG_ASSERT(m_graph, nullptr, m_graph.m_form == ThreadedCPS);
+
ScoreBoard scoreBoard(m_graph.m_nextMachineLocal);
scoreBoard.assertClear();
for (size_t blockIndex = 0; blockIndex < m_graph.numBlocks(); ++blockIndex) {
case GetMyArgumentsLength:
case GetLocal:
case SetLocal:
- case PutLocal:
- case KillLocal:
+ case PutStack:
+ case KillStack:
+ case GetStack:
case MovHint:
case ZombieHint:
case Phantom:
LValue jsValue = m_out.load64(addressFor(operand));
if (node) {
- DFG_ASSERT(m_graph, node, operand == node->variableAccessData()->machineLocal());
+ DFG_ASSERT(m_graph, node, operand == node->stackAccessData()->machineLocal);
// 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
case ExtractOSREntryLocal:
compileExtractOSREntryLocal();
break;
- case GetLocal:
- compileGetLocal();
+ case GetStack:
+ compileGetStack();
break;
- case PutLocal:
- compilePutLocal();
+ case PutStack:
+ compilePutStack();
break;
case GetMyArgumentsLength:
compileGetMyArgumentsLength();
case PutByOffsetHint:
case PutStructureHint:
case BottomValue:
- case KillLocal:
+ case KillStack:
break;
default:
DFG_CRASH(m_graph, m_node, "Unrecognized node in FTL backend");
setJSValue(m_out.load64(m_out.absolute(buffer + m_node->unlinkedLocal().toLocal())));
}
- void compileGetLocal()
+ void compileGetStack()
{
// GetLocals arise only for captured variables and arguments. For arguments, we might have
// already loaded it.
return;
}
- VariableAccessData* variable = m_node->variableAccessData();
- AbstractValue& value = m_state.variables().operand(variable->local());
+ StackAccessData* data = m_node->stackAccessData();
+ AbstractValue& value = m_state.variables().operand(data->local);
+
+ DFG_ASSERT(m_graph, m_node, isConcrete(data->format));
+ DFG_ASSERT(m_graph, m_node, data->format != FlushedDouble); // This just happens to not arise for GetStacks, right now. It would be trivial to support.
if (isInt32Speculation(value.m_type))
- setInt32(m_out.load32(payloadFor(variable->machineLocal())));
+ setInt32(m_out.load32(payloadFor(data->machineLocal)));
else
- setJSValue(m_out.load64(addressFor(variable->machineLocal())));
+ setJSValue(m_out.load64(addressFor(data->machineLocal)));
}
- void compilePutLocal()
+ void compilePutStack()
{
- VariableAccessData* variable = m_node->variableAccessData();
- switch (variable->flushFormat()) {
+ StackAccessData* data = m_node->stackAccessData();
+ switch (data->format) {
case FlushedJSValue:
case FlushedArguments: {
LValue value = lowJSValue(m_node->child1());
- m_out.store64(value, addressFor(variable->machineLocal()));
+ m_out.store64(value, addressFor(data->machineLocal));
break;
}
case FlushedDouble: {
LValue value = lowDouble(m_node->child1());
- m_out.storeDouble(value, addressFor(variable->machineLocal()));
+ m_out.storeDouble(value, addressFor(data->machineLocal));
break;
}
case FlushedInt32: {
LValue value = lowInt32(m_node->child1());
- m_out.store32(value, payloadFor(variable->machineLocal()));
+ m_out.store32(value, payloadFor(data->machineLocal));
break;
}
case FlushedInt52: {
LValue value = lowInt52(m_node->child1());
- m_out.store64(value, addressFor(variable->machineLocal()));
+ m_out.store64(value, addressFor(data->machineLocal));
break;
}
case FlushedCell: {
LValue value = lowCell(m_node->child1());
- m_out.store64(value, addressFor(variable->machineLocal()));
+ m_out.store64(value, addressFor(data->machineLocal));
break;
}
speculateBoolean(m_node->child1());
m_out.store64(
lowJSValue(m_node->child1(), ManualOperandSpeculation),
- addressFor(variable->machineLocal()));
+ addressFor(data->machineLocal));
break;
}
/*
- * Copyright (C) 2013, 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2013-2015 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
// Tracks one OSR exit site within the FTL JIT. OSR exit in FTL works by deconstructing
// the crazy that is OSR down to simple SSA CFG primitives that any compiler backend
-// (including of course LLVM) can grok and do meaningful things to. Except for
-// watchpoint-based exits, which haven't yet been implemented (see webkit.org/b/113647),
-// an exit is just a conditional branch in the emitted code where one destination is the
-// continuation and the other is a basic block that performs a no-return tail-call to an
-// exit thunk. This thunk takes as its arguments the live non-constant
-// not-already-accounted-for bytecode state. To appreciate how this works consider the
-// following JavaScript program, and its lowering down to LLVM IR including the relevant
-// exits:
+// (including of course LLVM) can grok and do meaningful things to. An exit is just a
+// conditional branch in the emitted code where one destination is the continuation and
+// the other is a basic block that performs a no-return tail-call to an exit thunk.
+// This thunk takes as its arguments the live non-constant not-already-accounted-for
+// bytecode state. To appreciate how this works consider the following JavaScript
+// program, and its lowering down to LLVM IR including the relevant exits:
//
// function foo(o) {
// var a = o.a; // predicted int
//
// BitOr(Check:Int32:@a, Int32:5)
//
-// Where @a is the node for the GetLocal node that gets the value of the 'a' variable.
-// Conceptually, this node can be further broken down to the following (note that this
-// particular lowering never actually happens - we skip this step and go straight to
-// LLVM IR - but it's still useful to see this):
+// Where @a is the node for the value of the 'a' variable. Conceptually, this node can
+// be further broken down to the following (note that this particular lowering never
+// actually happens - we skip this step and go straight to LLVM IR - but it's still
+// useful to see this):
//
// exitIf(@a is not int32);
// continuation;
// arguments into argument position), the backend could choose to simply inform us
// where it had placed the arguments and expect the callee (i.e. the exit thunk) to
// figure it out from there. It could also tell us what we need to do to pop stack,
-// although again, it doesn't have to; it could just emit that code normally. Though
-// we don't support this yet, we could; the only thing that would change on our end
-// is that we'd need feedback from the backend about the location of the arguments
-// and a description of the things that need to be done to pop stack. This would
-// involve switching the m_values array to use something more akin to ValueRecovery
-// rather than the current ExitValue, albeit possibly with some hacks to better
-// understand the kinds of places where the LLVM backend would put values.
+// although again, it doesn't have to; it could just emit that code normally. We do
+// all of these things through the patchpoint/stackmap LLVM intrinsics.
//
// - It could be extended to allow the backend to do its own exit hoisting, by using
// intrinsics (or meta-data, or something) to inform the backend that it's safe to
// make the predicate passed to 'exitIf()' more truthy.
-//
-// - It could be extended to support watchpoints (see webkit.org/b/113647) by making
-// the predicate passed to 'exitIf()' be an intrinsic that the backend knows to be
-// true at compile-time. The backend could then turn the conditional branch into a
-// replaceable jump, much like the DFG does.
struct OSRExit : public DFG::OSRExitBase {
OSRExit(
--- /dev/null
+function foo(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z) {
+ if (a)
+ return 42;
+ else
+ return 63;
+}
+
+function bar(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z) {
+ return foo(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z);
+}
+
+function baz(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z) {
+ return bar(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z);
+}
+
+function fuzz(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z) {
+ return baz(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z);
+}
+
+function buzz(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z) {
+ return fuzz(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z);
+}
+
+noInline(buzz);
+
+for (var i = 0; i < 10000; ++i)
+ buzz(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26);