Diff
Modified: trunk/Source/_javascript_Core/ChangeLog (153289 => 153290)
--- trunk/Source/_javascript_Core/ChangeLog 2013-07-25 04:05:18 UTC (rev 153289)
+++ trunk/Source/_javascript_Core/ChangeLog 2013-07-25 04:05:20 UTC (rev 153290)
@@ -1,5 +1,37 @@
2013-07-20 Filip Pizlo <[email protected]>
+ fourthTier: each DFG node that relies on other nodes to do their type checks should be able to tell you if those type checks happened
+ https://bugs.webkit.org/show_bug.cgi?id=118866
+
+ Reviewed by Sam Weinig.
+
+ Adds a safeToExecute() method that takes a node and an abstract state and tells you
+ if the node will run without crashing under that state.
+
+ * _javascript_Core.xcodeproj/project.pbxproj:
+ * bytecode/CodeBlock.cpp:
+ (JSC::CodeBlock::CodeBlock):
+ * dfg/DFGCFAPhase.cpp:
+ (CFAPhase):
+ (JSC::DFG::CFAPhase::CFAPhase):
+ (JSC::DFG::CFAPhase::run):
+ (JSC::DFG::CFAPhase::performBlockCFA):
+ (JSC::DFG::CFAPhase::performForwardCFA):
+ * dfg/DFGSafeToExecute.h: Added.
+ (DFG):
+ (SafeToExecuteEdge):
+ (JSC::DFG::SafeToExecuteEdge::SafeToExecuteEdge):
+ (JSC::DFG::SafeToExecuteEdge::operator()):
+ (JSC::DFG::SafeToExecuteEdge::result):
+ (JSC::DFG::safeToExecute):
+ * dfg/DFGStructureAbstractValue.h:
+ (JSC::DFG::StructureAbstractValue::isValidOffset):
+ (StructureAbstractValue):
+ * runtime/Options.h:
+ (JSC):
+
+2013-07-20 Filip Pizlo <[email protected]>
+
fourthTier: FTL should be able to generate LLVM IR that uses an intrinsic for OSR exit
https://bugs.webkit.org/show_bug.cgi?id=118948
Modified: trunk/Source/_javascript_Core/_javascript_Core.xcodeproj/project.pbxproj (153289 => 153290)
--- trunk/Source/_javascript_Core/_javascript_Core.xcodeproj/project.pbxproj 2013-07-25 04:05:18 UTC (rev 153289)
+++ trunk/Source/_javascript_Core/_javascript_Core.xcodeproj/project.pbxproj 2013-07-25 04:05:20 UTC (rev 153290)
@@ -839,6 +839,8 @@
A7E5AB381799E4B200D2833D /* LLVMDisassembler.h in Headers */ = {isa = PBXBuildFile; fileRef = A7E5AB341799E4B200D2833D /* LLVMDisassembler.h */; settings = {ATTRIBUTES = (Private, ); }; };
A7E5AB391799E4B200D2833D /* UDis86Disassembler.h in Headers */ = {isa = PBXBuildFile; fileRef = A7E5AB351799E4B200D2833D /* UDis86Disassembler.h */; settings = {ATTRIBUTES = (Private, ); }; };
A7E5AB3A1799E4B200D2833D /* X86Disassembler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A7E5AB361799E4B200D2833D /* X86Disassembler.cpp */; };
+ A7F2996B17A0BB670010417A /* FTLFail.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A7F2996917A0BB670010417A /* FTLFail.cpp */; };
+ A7F2996C17A0BB670010417A /* FTLFail.h in Headers */ = {isa = PBXBuildFile; fileRef = A7F2996A17A0BB670010417A /* FTLFail.h */; settings = {ATTRIBUTES = (Private, ); }; };
A7F9935F0FD7325100A0B2D0 /* JSONObject.h in Headers */ = {isa = PBXBuildFile; fileRef = A7F9935D0FD7325100A0B2D0 /* JSONObject.h */; };
A7F993600FD7325100A0B2D0 /* JSONObject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A7F9935E0FD7325100A0B2D0 /* JSONObject.cpp */; };
A7FB60A4103F7DC20017A286 /* PropertyDescriptor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A7FB60A3103F7DC20017A286 /* PropertyDescriptor.cpp */; };
@@ -1909,6 +1911,8 @@
A7E5AB341799E4B200D2833D /* LLVMDisassembler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LLVMDisassembler.h; path = disassembler/LLVMDisassembler.h; sourceTree = "<group>"; };
A7E5AB351799E4B200D2833D /* UDis86Disassembler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = UDis86Disassembler.h; path = disassembler/UDis86Disassembler.h; sourceTree = "<group>"; };
A7E5AB361799E4B200D2833D /* X86Disassembler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = X86Disassembler.cpp; path = disassembler/X86Disassembler.cpp; sourceTree = "<group>"; };
+ A7F2996917A0BB670010417A /* FTLFail.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FTLFail.cpp; path = ftl/FTLFail.cpp; sourceTree = "<group>"; };
+ A7F2996A17A0BB670010417A /* FTLFail.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FTLFail.h; path = ftl/FTLFail.h; sourceTree = "<group>"; };
A7F8690E0F9584A100558697 /* CachedCall.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CachedCall.h; sourceTree = "<group>"; };
A7F869EC0F95C2EC00558697 /* CallFrameClosure.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CallFrameClosure.h; sourceTree = "<group>"; };
A7F9935D0FD7325100A0B2D0 /* JSONObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSONObject.h; sourceTree = "<group>"; };
@@ -2169,9 +2173,9 @@
034768DFFF38A50411DB9C8B /* Products */ = {
isa = PBXGroup;
children = (
- 932F5BD90822A1C700736975 /* _javascript_Core.framework */,
932F5BE10822A1C700736975 /* jsc */,
0FF922CF14F46B130041A24E /* JSCLLIntOffsetsExtractor */,
+ 932F5BD90822A1C700736975 /* _javascript_Core.framework */,
141211200A48793C00480255 /* minidom */,
14BD59BF0A3E8F9000BAF59C /* testapi */,
6511230514046A4C002B101D /* testRegExp */,
@@ -2290,6 +2294,8 @@
0F235BC317178E1C00690C7F /* FTLExitThunkGenerator.h */,
0F235BC417178E1C00690C7F /* FTLExitValue.cpp */,
0F235BC517178E1C00690C7F /* FTLExitValue.h */,
+ A7F2996917A0BB670010417A /* FTLFail.cpp */,
+ A7F2996A17A0BB670010417A /* FTLFail.h */,
0FEA0A2B170B661900BB722C /* FTLFormattedValue.h */,
A78A977C179738D5009DF744 /* FTLGeneratedFunction.h */,
0FEA0A261709623B00BB722C /* FTLIntrinsicRepository.cpp */,
@@ -3623,6 +3629,7 @@
0F235BD717178E1C00690C7F /* FTLExitArgumentList.h in Headers */,
0F235BD917178E1C00690C7F /* FTLExitThunkGenerator.h in Headers */,
0F235BDB17178E1C00690C7F /* FTLExitValue.h in Headers */,
+ A7F2996C17A0BB670010417A /* FTLFail.h in Headers */,
0FEA0A2C170B661900BB722C /* FTLFormattedValue.h in Headers */,
A78A977F179738D5009DF744 /* FTLGeneratedFunction.h in Headers */,
0FEA0A241709606900BB722C /* FTLIntrinsicRepository.h in Headers */,
@@ -4455,6 +4462,7 @@
0F235BD517178E1C00690C7F /* FTLExitArgumentForOperand.cpp in Sources */,
0F235BD817178E1C00690C7F /* FTLExitThunkGenerator.cpp in Sources */,
0F235BDA17178E1C00690C7F /* FTLExitValue.cpp in Sources */,
+ A7F2996B17A0BB670010417A /* FTLFail.cpp in Sources */,
0FEA0A281709623B00BB722C /* FTLIntrinsicRepository.cpp in Sources */,
0FEA0A0D170513DB00BB722C /* FTLJITCode.cpp in Sources */,
A78A9780179738D5009DF744 /* FTLJITFinalizer.cpp in Sources */,
Modified: trunk/Source/_javascript_Core/bytecode/CodeBlock.cpp (153289 => 153290)
--- trunk/Source/_javascript_Core/bytecode/CodeBlock.cpp 2013-07-25 04:05:18 UTC (rev 153289)
+++ trunk/Source/_javascript_Core/bytecode/CodeBlock.cpp 2013-07-25 04:05:20 UTC (rev 153290)
@@ -1819,7 +1819,8 @@
|| Options::validateGraphAtEachPhase()
|| Options::verboseOSR()
|| Options::verboseCompilationQueue()
- || Options::reportCompileTimes())
+ || Options::reportCompileTimes()
+ || Options::verboseCFA())
hash();
if (Options::dumpGeneratedBytecodes())
Modified: trunk/Source/_javascript_Core/dfg/DFGCFAPhase.cpp (153289 => 153290)
--- trunk/Source/_javascript_Core/dfg/DFGCFAPhase.cpp 2013-07-25 04:05:18 UTC (rev 153289)
+++ trunk/Source/_javascript_Core/dfg/DFGCFAPhase.cpp 2013-07-25 04:05:20 UTC (rev 153290)
@@ -32,22 +32,18 @@
#include "DFGGraph.h"
#include "DFGInPlaceAbstractState.h"
#include "DFGPhase.h"
+#include "DFGSafeToExecute.h"
#include "Operations.h"
namespace JSC { namespace DFG {
class CFAPhase : public Phase {
public:
-#if DFG_ENABLE(DFG_PROPAGATION_VERBOSE)
- static const bool verbose = true;
-#else
- static const bool verbose = false;
-#endif
-
CFAPhase(Graph& graph)
: Phase(graph, "control flow analysis")
, m_state(graph)
, m_interpreter(graph, m_state)
+ , m_verbose(Options::verboseCFA())
{
}
@@ -59,6 +55,11 @@
m_count = 0;
+ if (m_verbose && !shouldDumpGraphAtEachPhase()) {
+ dataLog("Graph before CFA:\n");
+ m_graph.dump();
+ }
+
// This implements a pseudo-worklist-based forward CFA, except that the visit order
// of blocks is the bytecode program order (which is nearly topological), and
// instead of a worklist we just walk all basic blocks checking if cfaShouldRevisit
@@ -87,37 +88,42 @@
return;
if (!block->cfaShouldRevisit)
return;
- if (verbose)
+ if (m_verbose)
dataLog(" Block ", *block, ":\n");
m_state.beginBasicBlock(block);
- if (verbose) {
+ if (m_verbose) {
dataLogF(" head vars: ");
dumpOperands(block->valuesAtHead, WTF::dataFile());
dataLogF("\n");
}
for (unsigned i = 0; i < block->size(); ++i) {
- if (verbose) {
+ if (m_verbose) {
Node* node = block->at(i);
dataLogF(" %s @%u: ", Graph::opName(node->op()), node->index());
+
+ if (!safeToExecute(m_state, m_graph, node))
+ dataLog("(UNSAFE) ");
+
m_interpreter.dump(WTF::dataFile());
+
if (m_state.haveStructures())
dataLog(" (Have Structures)");
dataLogF("\n");
}
if (!m_interpreter.execute(i)) {
- if (verbose)
+ if (m_verbose)
dataLogF(" Expect OSR exit.\n");
break;
}
}
- if (verbose) {
+ if (m_verbose) {
dataLogF(" tail regs: ");
m_interpreter.dump(WTF::dataFile());
dataLogF("\n");
}
m_changed |= m_state.endBasicBlock(MergeToSuccessors);
- if (verbose) {
+ if (m_verbose) {
dataLogF(" tail vars: ");
dumpOperands(block->valuesAtTail, WTF::dataFile());
dataLogF("\n");
@@ -127,7 +133,7 @@
void performForwardCFA()
{
++m_count;
- if (verbose)
+ if (m_verbose)
dataLogF("CFA [%u]\n", ++m_count);
for (BlockIndex blockIndex = 0; blockIndex < m_graph.numBlocks(); ++blockIndex)
@@ -138,6 +144,8 @@
InPlaceAbstractState m_state;
AbstractInterpreter<InPlaceAbstractState> m_interpreter;
+ bool m_verbose;
+
bool m_changed;
unsigned m_count;
};
Added: trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h (0 => 153290)
--- trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h (rev 0)
+++ trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h 2013-07-25 04:05:20 UTC (rev 153290)
@@ -0,0 +1,285 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef DFGSafeToExecute_h
+#define DFGSafeToExecute_h
+
+#include <wtf/Platform.h>
+
+#if ENABLE(DFG_JIT)
+
+#include "DFGGraph.h"
+
+namespace JSC { namespace DFG {
+
+template<typename AbstractStateType>
+class SafeToExecuteEdge {
+public:
+ SafeToExecuteEdge(AbstractStateType& state)
+ : m_state(state)
+ , m_result(true)
+ {
+ }
+
+ void operator()(Node*, Edge edge)
+ {
+ switch (edge.useKind()) {
+ case UntypedUse:
+ case Int32Use:
+ case RealNumberUse:
+ case NumberUse:
+ case BooleanUse:
+ case CellUse:
+ case ObjectUse:
+ case ObjectOrOtherUse:
+ case StringIdentUse:
+ case StringUse:
+ case StringObjectUse:
+ case StringOrStringObjectUse:
+ case NotCellUse:
+ case OtherUse:
+ return;
+
+ case KnownInt32Use:
+ if (m_state.forNode(edge).m_type & ~SpecInt32)
+ m_result = false;
+ return;
+
+ case KnownNumberUse:
+ if (m_state.forNode(edge).m_type & ~SpecNumber)
+ m_result = false;
+ return;
+
+ case KnownCellUse:
+ if (m_state.forNode(edge).m_type & ~SpecCell)
+ m_result = false;
+ return;
+
+ case KnownStringUse:
+ if (m_state.forNode(edge).m_type & ~SpecString)
+ m_result = false;
+ return;
+
+ case LastUseKind:
+ RELEASE_ASSERT_NOT_REACHED();
+ break;
+ }
+ RELEASE_ASSERT_NOT_REACHED();
+ }
+
+ bool result() const { return m_result; }
+private:
+ AbstractStateType& m_state;
+ bool m_result;
+};
+
+// Determines if it's safe to execute a node within the given abstract state. This may
+// return false conservatively. If it returns true, then you can hoist the given node
+// up to the given point and expect that it will not crash. This doesn't guarantee that
+// the node will produce the result you wanted other than not crashing.
+template<typename AbstractStateType>
+bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node)
+{
+ SafeToExecuteEdge<AbstractStateType> safeToExecuteEdge(state);
+ DFG_NODE_DO_TO_CHILDREN(graph, node, safeToExecuteEdge);
+ if (!safeToExecuteEdge.result())
+ return false;
+
+ switch (node->op()) {
+ case JSConstant:
+ case WeakJSConstant:
+ case Identity:
+ case ToThis:
+ case CreateThis:
+ case GetCallee:
+ case SetCallee:
+ case GetLocal:
+ case SetLocal:
+ case MovHintAndCheck:
+ case MovHint:
+ case ZombieHint:
+ case GetArgument:
+ case Phantom:
+ case Upsilon:
+ case Phi:
+ case Flush:
+ case PhantomLocal:
+ case GetLocalUnlinked:
+ case SetArgument:
+ case InlineStart:
+ case BitAnd:
+ case BitOr:
+ case BitXor:
+ case BitLShift:
+ case BitRShift:
+ case BitURShift:
+ case ValueToInt32:
+ case UInt32ToNumber:
+ case Int32ToDouble:
+ case ForwardInt32ToDouble:
+ case DoubleAsInt32:
+ case ArithAdd:
+ case ArithSub:
+ case ArithNegate:
+ case ArithMul:
+ case ArithIMul:
+ case ArithDiv:
+ case ArithMod:
+ case ArithAbs:
+ case ArithMin:
+ case ArithMax:
+ case ArithSqrt:
+ case ValueAdd:
+ case GetById:
+ case GetByIdFlush:
+ case PutById:
+ case PutByIdDirect:
+ case CheckStructure:
+ case CheckExecutable:
+ case ForwardCheckStructure:
+ case GetButterfly:
+ case CheckArray:
+ case ForwardCheckArray:
+ case Arrayify:
+ case ArrayifyToStructure:
+ case GetScope:
+ case GetMyScope:
+ case SetMyScope:
+ case SkipTopScope:
+ case SkipScope:
+ case GetClosureRegisters:
+ case GetClosureVar:
+ case PutClosureVar:
+ case GetGlobalVar:
+ case PutGlobalVar:
+ case GlobalVarWatchpoint:
+ case VarInjectionWatchpoint:
+ case CheckFunction:
+ case AllocationProfileWatchpoint:
+ case RegExpExec:
+ case RegExpTest:
+ case CompareLess:
+ case CompareLessEq:
+ case CompareGreater:
+ case CompareGreaterEq:
+ case CompareEq:
+ case CompareEqConstant:
+ case CompareStrictEq:
+ case CompareStrictEqConstant:
+ case Call:
+ case Construct:
+ case NewObject:
+ case NewArray:
+ case NewArrayWithSize:
+ case NewArrayBuffer:
+ case NewRegexp:
+ case Breakpoint:
+ case CheckHasInstance:
+ case InstanceOf:
+ case IsUndefined:
+ case IsBoolean:
+ case IsNumber:
+ case IsString:
+ case IsObject:
+ case IsFunction:
+ case TypeOf:
+ case LogicalNot:
+ case ToPrimitive:
+ case ToString:
+ case NewStringObject:
+ case MakeRope:
+ case In:
+ case CreateActivation:
+ case TearOffActivation:
+ case CreateArguments:
+ case PhantomArguments:
+ case TearOffArguments:
+ case GetMyArgumentsLength:
+ case GetMyArgumentByVal:
+ case GetMyArgumentsLengthSafe:
+ case GetMyArgumentByValSafe:
+ case CheckArgumentsNotCreated:
+ case NewFunctionNoCheck:
+ case NewFunction:
+ case NewFunctionExpression:
+ case Jump:
+ case Branch:
+ case Switch:
+ case Return:
+ case Throw:
+ case ThrowReferenceError:
+ case CountExecution:
+ case ForceOSRExit:
+ case ForwardForceOSRExit:
+ case CheckWatchdogTimer:
+ case StringFromCharCode:
+ return true;
+
+ case GetByVal:
+ case GetIndexedPropertyStorage:
+ case GetArrayLength:
+ case ArrayPush:
+ case ArrayPop:
+ case StringCharAt:
+ case StringCharCodeAt:
+ return node->arrayMode().alreadyChecked(graph, node, state.forNode(node->child1()));
+
+ case PutByVal:
+ case PutByValAlias:
+ return node->arrayMode().modeForPut().alreadyChecked(
+ graph, node, state.forNode(graph.varArgChild(node, 0)));
+
+ case StructureTransitionWatchpoint:
+ case ForwardStructureTransitionWatchpoint:
+ return state.forNode(node->child1()).m_futurePossibleStructure.isSubsetOf(
+ StructureSet(node->structure()));
+
+ case PutStructure:
+ case PhantomPutStructure:
+ case AllocatePropertyStorage:
+ case ReallocatePropertyStorage:
+ return state.forNode(node->child1()).m_currentKnownStructure.isSubsetOf(
+ StructureSet(node->structureTransitionData().previousStructure));
+
+ case GetByOffset:
+ case PutByOffset:
+ return state.forNode(node->child1()).m_currentKnownStructure.isValidOffset(
+ graph.m_storageAccessData[node->storageAccessDataIndex()].offset);
+
+ case LastNodeType:
+ RELEASE_ASSERT_NOT_REACHED();
+ return false;
+ }
+
+ RELEASE_ASSERT_NOT_REACHED();
+ return false;
+}
+
+} } // namespace JSC::DFG
+
+#endif // ENABLE(DFG_JIT)
+
+#endif // DFGSafeToExecute_h
+
Modified: trunk/Source/_javascript_Core/dfg/DFGStructureAbstractValue.h (153289 => 153290)
--- trunk/Source/_javascript_Core/dfg/DFGStructureAbstractValue.h 2013-07-25 04:05:18 UTC (rev 153289)
+++ trunk/Source/_javascript_Core/dfg/DFGStructureAbstractValue.h 2013-07-25 04:05:20 UTC (rev 153290)
@@ -282,6 +282,15 @@
return speculationFromStructure(m_structure);
}
+ bool isValidOffset(PropertyOffset offset)
+ {
+ if (isTop())
+ return false;
+ if (isClear())
+ return true;
+ return m_structure->isValidOffset(offset);
+ }
+
bool hasSingleton() const
{
return isNeitherClearNorTop();
Modified: trunk/Source/_javascript_Core/runtime/Options.h (153289 => 153290)
--- trunk/Source/_javascript_Core/runtime/Options.h 2013-07-25 04:05:18 UTC (rev 153289)
+++ trunk/Source/_javascript_Core/runtime/Options.h 2013-07-25 04:05:20 UTC (rev 153290)
@@ -113,6 +113,7 @@
v(bool, verboseCallLink, false) \
v(bool, verboseCompilationQueue, false) \
v(bool, reportCompileTimes, false) \
+ v(bool, verboseCFA, false) \
\
v(bool, enableOSREntryInLoops, true) \
\