summaryrefslogtreecommitdiff
path: root/Source/JavaScriptCore/dfg/DFGSafeToExecute.h
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
commit1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch)
tree46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/JavaScriptCore/dfg/DFGSafeToExecute.h
parent32761a6cee1d0dee366b885b7b9c777e67885688 (diff)
downloadWebKitGtk-tarball-master.tar.gz
Diffstat (limited to 'Source/JavaScriptCore/dfg/DFGSafeToExecute.h')
-rw-r--r--Source/JavaScriptCore/dfg/DFGSafeToExecute.h320
1 files changed, 254 insertions, 66 deletions
diff --git a/Source/JavaScriptCore/dfg/DFGSafeToExecute.h b/Source/JavaScriptCore/dfg/DFGSafeToExecute.h
index b6cd5dc08..a9e6a6b89 100644
--- a/Source/JavaScriptCore/dfg/DFGSafeToExecute.h
+++ b/Source/JavaScriptCore/dfg/DFGSafeToExecute.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013, 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2013-2016 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -23,10 +23,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef DFGSafeToExecute_h
-#define DFGSafeToExecute_h
-
-#include <wtf/Platform.h>
+#pragma once
#if ENABLE(DFG_JIT)
@@ -48,29 +45,45 @@ public:
switch (edge.useKind()) {
case UntypedUse:
case Int32Use:
- case RealNumberUse:
+ case DoubleRepUse:
+ case DoubleRepRealUse:
+ case Int52RepUse:
case NumberUse:
+ case RealNumberUse:
case BooleanUse:
case CellUse:
+ case CellOrOtherUse:
case ObjectUse:
+ case ArrayUse:
+ case FunctionUse:
case FinalObjectUse:
+ case RegExpObjectUse:
+ case ProxyObjectUse:
+ case DerivedArrayUse:
+ case MapObjectUse:
+ case SetObjectUse:
case ObjectOrOtherUse:
case StringIdentUse:
case StringUse:
+ case StringOrOtherUse:
+ case SymbolUse:
case StringObjectUse:
case StringOrStringObjectUse:
+ case NotStringVarUse:
case NotCellUse:
case OtherUse:
- case MachineIntUse:
+ case MiscUse:
+ case AnyIntUse:
+ case DoubleRepAnyIntUse:
return;
case KnownInt32Use:
- if (m_state.forNode(edge).m_type & ~SpecInt32)
+ if (m_state.forNode(edge).m_type & ~SpecInt32Only)
m_result = false;
return;
-
- case KnownNumberUse:
- if (m_state.forNode(edge).m_type & ~SpecFullNumber)
+
+ case KnownBooleanUse:
+ if (m_state.forNode(edge).m_type & ~SpecBoolean)
m_result = false;
return;
@@ -83,6 +96,11 @@ public:
if (m_state.forNode(edge).m_type & ~SpecString)
m_result = false;
return;
+
+ case KnownPrimitiveUse:
+ if (m_state.forNode(edge).m_type & ~(SpecHeapTop & ~SpecObject))
+ m_result = false;
+ return;
case LastUseKind:
RELEASE_ASSERT_NOT_REACHED();
@@ -99,8 +117,15 @@ private:
// 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.
+// up to the given point and expect that it will not crash. It also guarantees that the
+// node will not produce a malformed JSValue or object pointer when executed in the
+// given state. But this doesn't guarantee that the node will produce the result you
+// wanted. For example, you may have a GetByOffset from a prototype that only makes
+// semantic sense if you've also checked that some nearer prototype doesn't also have
+// a property of the same name. This could still return true even if that check hadn't
+// been performed in the given abstract state. That's fine though: the load can still
+// safely execute before that check, so long as that check continues to guard any
+// user-observable things done to the loaded value.
template<typename AbstractStateType>
bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node)
{
@@ -109,18 +134,28 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node)
if (!safeToExecuteEdge.result())
return false;
+ // NOTE: This tends to lie when it comes to effectful nodes, because it knows that they aren't going to
+ // get hoisted anyway.
+
switch (node->op()) {
case JSConstant:
- case WeakJSConstant:
+ case DoubleConstant:
+ case Int52Constant:
+ case LazyJSConstant:
case Identity:
case ToThis:
case CreateThis:
case GetCallee:
+ case GetArgumentCountIncludingThis:
+ case GetRestLength:
case GetLocal:
case SetLocal:
+ case PutStack:
+ case KillStack:
+ case GetStack:
case MovHint:
case ZombieHint:
- case GetArgument:
+ case ExitOK:
case Phantom:
case Upsilon:
case Phi:
@@ -136,9 +171,9 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node)
case BitURShift:
case ValueToInt32:
case UInt32ToNumber:
- case Int32ToDouble:
case DoubleAsInt32:
case ArithAdd:
+ case ArithClz32:
case ArithSub:
case ArithNegate:
case ArithMul:
@@ -148,33 +183,59 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node)
case ArithAbs:
case ArithMin:
case ArithMax:
+ case ArithPow:
+ case ArithRandom:
case ArithSqrt:
+ case ArithFRound:
+ case ArithRound:
+ case ArithFloor:
+ case ArithCeil:
+ case ArithTrunc:
case ArithSin:
case ArithCos:
+ case ArithTan:
+ case ArithLog:
case ValueAdd:
+ case TryGetById:
+ case DeleteById:
+ case DeleteByVal:
case GetById:
+ case GetByIdWithThis:
+ case GetByValWithThis:
case GetByIdFlush:
case PutById:
+ case PutByIdFlush:
+ case PutByIdWithThis:
+ case PutByValWithThis:
case PutByIdDirect:
+ case PutGetterById:
+ case PutSetterById:
+ case PutGetterSetterById:
+ case PutGetterByVal:
+ case PutSetterByVal:
+ case DefineDataProperty:
+ case DefineAccessorProperty:
case CheckStructure:
- case CheckExecutable:
+ case GetExecutable:
case GetButterfly:
+ case CallDOMGetter:
+ case CallDOM:
+ case CheckDOM:
case CheckArray:
case Arrayify:
case ArrayifyToStructure:
case GetScope:
- case GetMyScope:
- case SkipTopScope:
case SkipScope:
- case GetClosureRegisters:
+ case GetGlobalObject:
case GetClosureVar:
case PutClosureVar:
case GetGlobalVar:
- case PutGlobalVar:
- case VariableWatchpoint:
- case VarInjectionWatchpoint:
- case CheckFunction:
- case AllocationProfileWatchpoint:
+ case GetGlobalLexicalVariable:
+ case PutGlobalVariable:
+ case CheckCell:
+ case CheckBadCell:
+ case CheckNotEmpty:
+ case CheckStringIdent:
case RegExpExec:
case RegExpTest:
case CompareLess:
@@ -182,56 +243,84 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node)
case CompareGreater:
case CompareGreaterEq:
case CompareEq:
- case CompareEqConstant:
case CompareStrictEq:
- case CompareStrictEqConstant:
+ case CompareEqPtr:
case Call:
+ case DirectCall:
+ case TailCallInlinedCaller:
+ case DirectTailCallInlinedCaller:
case Construct:
+ case DirectConstruct:
+ case CallVarargs:
+ case CallEval:
+ case TailCallVarargsInlinedCaller:
+ case TailCallForwardVarargsInlinedCaller:
+ case ConstructVarargs:
+ case LoadVarargs:
+ case CallForwardVarargs:
+ case ConstructForwardVarargs:
case NewObject:
case NewArray:
case NewArrayWithSize:
case NewArrayBuffer:
+ case NewArrayWithSpread:
+ case Spread:
case NewRegexp:
- case Breakpoint:
- case ProfileWillCall:
- case ProfileDidCall:
- case CheckHasInstance:
+ case ProfileType:
+ case ProfileControlFlow:
+ case CheckTypeInfoFlags:
+ case ParseInt:
+ case OverridesHasInstance:
case InstanceOf:
+ case InstanceOfCustom:
+ case IsEmpty:
case IsUndefined:
case IsBoolean:
case IsNumber:
- case IsString:
case IsObject:
+ case IsObjectOrNull:
case IsFunction:
+ case IsCellWithType:
+ case IsTypedArrayView:
case TypeOf:
case LogicalNot:
+ case CallObjectConstructor:
case ToPrimitive:
case ToString:
+ case ToNumber:
+ case NumberToStringWithRadix:
+ case SetFunctionName:
+ case StrCat:
+ case CallStringConstructor:
case NewStringObject:
case MakeRope:
case In:
+ case HasOwnProperty:
case CreateActivation:
- case TearOffActivation:
- case CreateArguments:
- case PhantomArguments:
- case TearOffArguments:
- case GetMyArgumentsLength:
- case GetMyArgumentByVal:
- case GetMyArgumentsLengthSafe:
- case GetMyArgumentByValSafe:
- case CheckArgumentsNotCreated:
- case NewFunctionNoCheck:
+ case CreateDirectArguments:
+ case CreateScopedArguments:
+ case CreateClonedArguments:
+ case GetFromArguments:
+ case GetArgument:
+ case PutToArguments:
case NewFunction:
- case NewFunctionExpression:
+ case NewGeneratorFunction:
+ case NewAsyncFunction:
case Jump:
case Branch:
case Switch:
case Return:
+ case TailCall:
+ case DirectTailCall:
+ case TailCallVarargs:
+ case TailCallForwardVarargs:
case Throw:
- case ThrowReferenceError:
+ case ThrowStaticError:
case CountExecution:
case ForceOSRExit:
case CheckWatchdogTimer:
+ case LogShadowChickenPrologue:
+ case LogShadowChickenTail:
case StringFromCharCode:
case NewTypedArray:
case Unreachable:
@@ -240,20 +329,83 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node)
case CheckTierUpAtReturn:
case CheckTierUpAndOSREnter:
case LoopHint:
- case Int52ToDouble:
- case Int52ToValue:
- case StoreBarrier:
- case ConditionalStoreBarrier:
- case StoreBarrierWithNullCheck:
case InvalidationPoint:
case NotifyWrite:
- case FunctionReentryWatchpoint:
- case TypedArrayWatchpoint:
case CheckInBounds:
case ConstantStoragePointer:
case Check:
+ case MultiPutByOffset:
+ case ValueRep:
+ case DoubleRep:
+ case Int52Rep:
+ case BooleanToNumber:
+ case FiatInt52:
+ case GetGetter:
+ case GetSetter:
+ case GetEnumerableLength:
+ case HasGenericProperty:
+ case HasStructureProperty:
+ case HasIndexedProperty:
+ case GetDirectPname:
+ case GetPropertyEnumerator:
+ case GetEnumeratorStructurePname:
+ case GetEnumeratorGenericPname:
+ case ToIndexString:
+ case PhantomNewObject:
+ case PhantomNewFunction:
+ case PhantomNewGeneratorFunction:
+ case PhantomNewAsyncFunction:
+ case PhantomCreateActivation:
+ case PutHint:
+ case CheckStructureImmediate:
+ case MaterializeNewObject:
+ case MaterializeCreateActivation:
+ case PhantomDirectArguments:
+ case PhantomCreateRest:
+ case PhantomSpread:
+ case PhantomNewArrayWithSpread:
+ case PhantomClonedArguments:
+ case GetMyArgumentByVal:
+ case GetMyArgumentByValOutOfBounds:
+ case ForwardVarargs:
+ case CreateRest:
+ case StringReplace:
+ case StringReplaceRegExp:
+ case GetRegExpObjectLastIndex:
+ case SetRegExpObjectLastIndex:
+ case RecordRegExpCachedResult:
+ case GetDynamicVar:
+ case PutDynamicVar:
+ case ResolveScope:
+ case MapHash:
+ case ToLowerCase:
+ case GetMapBucket:
+ case LoadFromJSMapBucket:
+ case IsNonEmptyMapBucket:
return true;
-
+
+ case ArraySlice: {
+ // You could plausibly move this code around as long as you proved the
+ // incoming array base structure is an original array at the hoisted location.
+ // Instead of doing that extra work, we just conservatively return false.
+ return false;
+ }
+
+ case BottomValue:
+ // If in doubt, assume that this isn't safe to execute, just because we have no way of
+ // compiling this node.
+ return false;
+
+ case StoreBarrier:
+ case FencedStoreBarrier:
+ case PutStructure:
+ case NukeStructureAndSetButterfly:
+ // We conservatively assume that these cannot be put anywhere, which forces the compiler to
+ // keep them exactly where they were. This is sort of overkill since the clobberize effects
+ // already force these things to be ordered precisely. I'm just not confident enough in my
+ // effect based memory model to rely solely on that right now.
+ return false;
+
case GetByVal:
case GetIndexedPropertyStorage:
case GetArrayLength:
@@ -272,22 +424,61 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node)
return node->arrayMode().modeForPut().alreadyChecked(
graph, node, state.forNode(graph.varArgChild(node, 0)));
- case StructureTransitionWatchpoint:
- 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));
+ return state.forNode(node->child1()).m_structure.isSubsetOf(
+ RegisteredStructureSet(node->transition()->previous));
case GetByOffset:
- case PutByOffset:
- return state.forNode(node->child1()).m_currentKnownStructure.isValidOffset(
- graph.m_storageAccessData[node->storageAccessDataIndex()].offset);
+ case GetGetterSetterByOffset:
+ case PutByOffset: {
+ PropertyOffset offset = node->storageAccessData().offset;
+
+ if (state.structureClobberState() == StructuresAreWatched) {
+ if (JSObject* knownBase = node->child1()->dynamicCastConstant<JSObject*>(graph.m_vm)) {
+ if (graph.isSafeToLoad(knownBase, offset))
+ return true;
+ }
+ }
+ StructureAbstractValue& value = state.forNode(node->child1()).m_structure;
+ if (value.isInfinite())
+ return false;
+ for (unsigned i = value.size(); i--;) {
+ if (!value[i]->isValidOffset(offset))
+ return false;
+ }
+ return true;
+ }
+
+ case MultiGetByOffset: {
+ // We can't always guarantee that the MultiGetByOffset is safe to execute if it
+ // contains loads from prototypes. If the load requires a check in IR, which is rare, then
+ // we currently claim that we don't know if it's safe to execute because finding that
+ // check in the abstract state would be hard. If the load requires watchpoints, we just
+ // check if we're not in a clobbered state (i.e. in between a side effect and an
+ // invalidation point).
+ for (const MultiGetByOffsetCase& getCase : node->multiGetByOffsetData().cases) {
+ GetByOffsetMethod method = getCase.method();
+ switch (method.kind()) {
+ case GetByOffsetMethod::Invalid:
+ RELEASE_ASSERT_NOT_REACHED();
+ break;
+ case GetByOffsetMethod::Constant: // OK because constants are always safe to execute.
+ case GetByOffsetMethod::Load: // OK because the MultiGetByOffset has its own checks for loading from self.
+ break;
+ case GetByOffsetMethod::LoadFromPrototype:
+ // Only OK if the state isn't clobbered. That's almost always the case.
+ if (state.structureClobberState() != StructuresAreWatched)
+ return false;
+ if (!graph.isSafeToLoad(method.prototype()->cast<JSObject*>(), method.offset()))
+ return false;
+ break;
+ }
+ }
+ return true;
+ }
+
case LastNodeType:
RELEASE_ASSERT_NOT_REACHED();
return false;
@@ -300,6 +491,3 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node)
} } // namespace JSC::DFG
#endif // ENABLE(DFG_JIT)
-
-#endif // DFGSafeToExecute_h
-