2017-08-12 16:48:01 +00:00
|
|
|
/*
|
2022-10-23 02:55:20 +00:00
|
|
|
* Copyright (C) 2013-2020 Apple Inc. All rights reserved.
|
2017-08-12 16:48:01 +00:00
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
|
|
|
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
|
|
|
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
|
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
|
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
|
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
|
|
|
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#if ENABLE(DFG_JIT)
|
|
|
|
|
|
|
|
#include "DFGGraph.h"
|
|
|
|
|
|
|
|
namespace JSC { namespace DFG {
|
|
|
|
|
2022-10-23 02:55:20 +00:00
|
|
|
// This phase is used to determine if a node can safely run at a new location.
|
|
|
|
// It is important to note that returning false does not mean it's definitely
|
|
|
|
// wrong to run the node at the new location. In other words, returning false
|
|
|
|
// does not imply moving the node would be invalid only that this phase could
|
|
|
|
// not prove it is valid. Thus, it is always ok to return false.
|
|
|
|
|
2017-08-12 16:48:01 +00:00
|
|
|
template<typename AbstractStateType>
|
|
|
|
class SafeToExecuteEdge {
|
|
|
|
public:
|
|
|
|
SafeToExecuteEdge(AbstractStateType& state)
|
|
|
|
: m_state(state)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void operator()(Node*, Edge edge)
|
|
|
|
{
|
2020-08-29 13:27:11 +00:00
|
|
|
m_maySeeEmptyChild |= !!(m_state.forNode(edge).m_type & SpecEmpty);
|
|
|
|
|
2017-08-12 16:48:01 +00:00
|
|
|
switch (edge.useKind()) {
|
|
|
|
case UntypedUse:
|
|
|
|
case Int32Use:
|
|
|
|
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:
|
2022-10-23 02:55:20 +00:00
|
|
|
case PromiseObjectUse:
|
2017-08-12 16:48:01 +00:00
|
|
|
case ProxyObjectUse:
|
|
|
|
case DerivedArrayUse:
|
2022-10-23 02:55:20 +00:00
|
|
|
case DateObjectUse:
|
2017-08-12 16:48:01 +00:00
|
|
|
case MapObjectUse:
|
|
|
|
case SetObjectUse:
|
2020-08-29 13:27:11 +00:00
|
|
|
case WeakMapObjectUse:
|
|
|
|
case WeakSetObjectUse:
|
|
|
|
case DataViewObjectUse:
|
2017-08-12 16:48:01 +00:00
|
|
|
case ObjectOrOtherUse:
|
|
|
|
case StringIdentUse:
|
|
|
|
case StringUse:
|
|
|
|
case StringOrOtherUse:
|
|
|
|
case SymbolUse:
|
2022-10-23 02:55:20 +00:00
|
|
|
case AnyBigIntUse:
|
|
|
|
case HeapBigIntUse:
|
|
|
|
case BigInt32Use:
|
2017-08-12 16:48:01 +00:00
|
|
|
case StringObjectUse:
|
|
|
|
case StringOrStringObjectUse:
|
|
|
|
case NotStringVarUse:
|
2020-08-29 13:27:11 +00:00
|
|
|
case NotSymbolUse:
|
2017-08-12 16:48:01 +00:00
|
|
|
case NotCellUse:
|
2022-10-23 02:55:20 +00:00
|
|
|
case NotCellNorBigIntUse:
|
2017-08-12 16:48:01 +00:00
|
|
|
case OtherUse:
|
|
|
|
case MiscUse:
|
|
|
|
case AnyIntUse:
|
|
|
|
case DoubleRepAnyIntUse:
|
|
|
|
return;
|
|
|
|
|
|
|
|
case KnownInt32Use:
|
|
|
|
if (m_state.forNode(edge).m_type & ~SpecInt32Only)
|
|
|
|
m_result = false;
|
|
|
|
return;
|
|
|
|
|
|
|
|
case KnownBooleanUse:
|
|
|
|
if (m_state.forNode(edge).m_type & ~SpecBoolean)
|
|
|
|
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 KnownPrimitiveUse:
|
|
|
|
if (m_state.forNode(edge).m_type & ~(SpecHeapTop & ~SpecObject))
|
|
|
|
m_result = false;
|
|
|
|
return;
|
2020-08-29 13:27:11 +00:00
|
|
|
|
|
|
|
case KnownOtherUse:
|
|
|
|
if (m_state.forNode(edge).m_type & ~SpecOther)
|
|
|
|
m_result = false;
|
|
|
|
return;
|
2017-08-12 16:48:01 +00:00
|
|
|
|
|
|
|
case LastUseKind:
|
|
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool result() const { return m_result; }
|
2020-08-29 13:27:11 +00:00
|
|
|
bool maySeeEmptyChild() const { return m_maySeeEmptyChild; }
|
2017-08-12 16:48:01 +00:00
|
|
|
private:
|
|
|
|
AbstractStateType& m_state;
|
2020-08-29 13:27:11 +00:00
|
|
|
bool m_result { true };
|
|
|
|
bool m_maySeeEmptyChild { false };
|
2017-08-12 16:48:01 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// 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. 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>
|
2020-08-29 13:27:11 +00:00
|
|
|
bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node, bool ignoreEmptyChildren = false)
|
2017-08-12 16:48:01 +00:00
|
|
|
{
|
|
|
|
SafeToExecuteEdge<AbstractStateType> safeToExecuteEdge(state);
|
|
|
|
DFG_NODE_DO_TO_CHILDREN(graph, node, safeToExecuteEdge);
|
|
|
|
if (!safeToExecuteEdge.result())
|
|
|
|
return false;
|
|
|
|
|
2020-08-29 13:27:11 +00:00
|
|
|
if (!ignoreEmptyChildren && safeToExecuteEdge.maySeeEmptyChild()) {
|
|
|
|
// We conservatively assume if the empty value flows into a node,
|
|
|
|
// it might not be able to handle it (e.g, crash). In general, the bytecode generator
|
|
|
|
// emits code in such a way that most node types don't need to worry about the empty value
|
|
|
|
// because they will never see it. However, code motion has to consider the empty
|
|
|
|
// value so it does not insert/move nodes to a place where they will crash. E.g, the
|
|
|
|
// type check hoisting phase needs to insert CheckStructureOrEmpty instead of CheckStructure
|
|
|
|
// for hoisted structure checks because it can not guarantee that a particular local is not
|
|
|
|
// the empty value.
|
|
|
|
switch (node->op()) {
|
|
|
|
case CheckNotEmpty:
|
|
|
|
case CheckStructureOrEmpty:
|
2022-10-23 02:55:20 +00:00
|
|
|
case CheckArrayOrEmpty:
|
2020-08-29 13:27:11 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-23 02:55:20 +00:00
|
|
|
// NOTE: This can lie when it comes to effectful nodes, because it knows that they aren't going to
|
|
|
|
// get hoisted anyway. Sometimes this is convenient so we can avoid branching on some internal
|
|
|
|
// state of the node (like what some child's UseKind might be). However, nodes that are obviously
|
|
|
|
// always effectful, we return false for, to make auditing the "return true" cases easier.
|
2017-08-12 16:48:01 +00:00
|
|
|
|
|
|
|
switch (node->op()) {
|
|
|
|
case JSConstant:
|
|
|
|
case DoubleConstant:
|
|
|
|
case Int52Constant:
|
|
|
|
case LazyJSConstant:
|
|
|
|
case Identity:
|
2020-08-29 13:27:11 +00:00
|
|
|
case IdentityWithProfile:
|
2017-08-12 16:48:01 +00:00
|
|
|
case GetCallee:
|
|
|
|
case GetArgumentCountIncludingThis:
|
|
|
|
case GetRestLength:
|
|
|
|
case GetLocal:
|
|
|
|
case GetStack:
|
|
|
|
case ExitOK:
|
|
|
|
case Phantom:
|
2020-08-29 13:27:11 +00:00
|
|
|
case ArithBitNot:
|
|
|
|
case ArithBitAnd:
|
|
|
|
case ArithBitOr:
|
|
|
|
case ArithBitXor:
|
|
|
|
case ArithBitLShift:
|
2022-10-23 02:55:20 +00:00
|
|
|
case ArithBitRShift:
|
2017-08-12 16:48:01 +00:00
|
|
|
case BitURShift:
|
|
|
|
case ValueToInt32:
|
|
|
|
case UInt32ToNumber:
|
|
|
|
case DoubleAsInt32:
|
|
|
|
case ArithAdd:
|
|
|
|
case ArithClz32:
|
|
|
|
case ArithSub:
|
|
|
|
case ArithNegate:
|
|
|
|
case ArithMul:
|
|
|
|
case ArithDiv:
|
|
|
|
case ArithMod:
|
|
|
|
case ArithAbs:
|
|
|
|
case ArithMin:
|
|
|
|
case ArithMax:
|
|
|
|
case ArithPow:
|
|
|
|
case ArithSqrt:
|
|
|
|
case ArithFRound:
|
|
|
|
case ArithRound:
|
|
|
|
case ArithFloor:
|
|
|
|
case ArithCeil:
|
|
|
|
case ArithTrunc:
|
2018-01-03 05:16:05 +00:00
|
|
|
case ArithUnary:
|
2017-08-12 16:48:01 +00:00
|
|
|
case CheckStructure:
|
2020-08-29 13:27:11 +00:00
|
|
|
case CheckStructureOrEmpty:
|
2017-08-12 16:48:01 +00:00
|
|
|
case GetExecutable:
|
2022-10-23 02:55:20 +00:00
|
|
|
case CheckJSCast:
|
|
|
|
case CheckNotJSCast:
|
2017-08-12 16:48:01 +00:00
|
|
|
case CheckArray:
|
2022-10-23 02:55:20 +00:00
|
|
|
case CheckArrayOrEmpty:
|
2017-08-12 16:48:01 +00:00
|
|
|
case GetScope:
|
|
|
|
case SkipScope:
|
|
|
|
case GetGlobalObject:
|
2020-08-29 13:27:11 +00:00
|
|
|
case GetGlobalThis:
|
2017-08-12 16:48:01 +00:00
|
|
|
case GetClosureVar:
|
|
|
|
case GetGlobalVar:
|
|
|
|
case GetGlobalLexicalVariable:
|
2022-10-23 02:55:20 +00:00
|
|
|
case CheckIsConstant:
|
2017-08-12 16:48:01 +00:00
|
|
|
case CheckNotEmpty:
|
2020-08-29 13:27:11 +00:00
|
|
|
case AssertNotEmpty:
|
2022-10-23 02:55:20 +00:00
|
|
|
case CheckIdent:
|
2017-08-12 16:48:01 +00:00
|
|
|
case CompareLess:
|
|
|
|
case CompareLessEq:
|
|
|
|
case CompareGreater:
|
|
|
|
case CompareGreaterEq:
|
2020-08-29 13:27:11 +00:00
|
|
|
case CompareBelow:
|
|
|
|
case CompareBelowEq:
|
2017-08-12 16:48:01 +00:00
|
|
|
case CompareEq:
|
|
|
|
case CompareStrictEq:
|
|
|
|
case CompareEqPtr:
|
2020-08-29 13:27:11 +00:00
|
|
|
case SameValue:
|
2017-08-12 16:48:01 +00:00
|
|
|
case CheckTypeInfoFlags:
|
2018-01-03 05:16:05 +00:00
|
|
|
case ParseInt:
|
2017-08-12 16:48:01 +00:00
|
|
|
case OverridesHasInstance:
|
|
|
|
case IsEmpty:
|
2022-10-23 02:55:20 +00:00
|
|
|
case TypeOfIsUndefined:
|
|
|
|
case TypeOfIsObject:
|
|
|
|
case TypeOfIsFunction:
|
2020-08-29 13:27:11 +00:00
|
|
|
case IsUndefinedOrNull:
|
2017-08-12 16:48:01 +00:00
|
|
|
case IsBoolean:
|
|
|
|
case IsNumber:
|
2022-10-23 02:55:20 +00:00
|
|
|
case IsBigInt:
|
2020-08-29 13:27:11 +00:00
|
|
|
case NumberIsInteger:
|
2017-08-12 16:48:01 +00:00
|
|
|
case IsObject:
|
2022-10-23 02:55:20 +00:00
|
|
|
case IsCallable:
|
|
|
|
case IsConstructor:
|
2017-08-12 16:48:01 +00:00
|
|
|
case IsCellWithType:
|
|
|
|
case IsTypedArrayView:
|
|
|
|
case TypeOf:
|
|
|
|
case LogicalNot:
|
|
|
|
case ToString:
|
2020-08-29 13:27:11 +00:00
|
|
|
case NumberToStringWithValidRadixConstant:
|
2017-08-12 16:48:01 +00:00
|
|
|
case StrCat:
|
|
|
|
case CallStringConstructor:
|
|
|
|
case MakeRope:
|
|
|
|
case GetFromArguments:
|
|
|
|
case GetArgument:
|
|
|
|
case StringFromCharCode:
|
|
|
|
case ExtractOSREntryLocal:
|
2020-08-29 13:27:11 +00:00
|
|
|
case ExtractCatchLocal:
|
2022-10-23 02:55:20 +00:00
|
|
|
case AssertInBounds:
|
2017-08-12 16:48:01 +00:00
|
|
|
case CheckInBounds:
|
|
|
|
case ConstantStoragePointer:
|
|
|
|
case Check:
|
2020-08-29 13:27:11 +00:00
|
|
|
case CheckVarargs:
|
2017-08-12 16:48:01 +00:00
|
|
|
case ValueRep:
|
|
|
|
case DoubleRep:
|
|
|
|
case Int52Rep:
|
|
|
|
case BooleanToNumber:
|
|
|
|
case FiatInt52:
|
|
|
|
case HasIndexedProperty:
|
2022-10-23 02:55:20 +00:00
|
|
|
case HasEnumerableIndexedProperty:
|
2017-08-12 16:48:01 +00:00
|
|
|
case GetEnumeratorStructurePname:
|
|
|
|
case GetEnumeratorGenericPname:
|
|
|
|
case ToIndexString:
|
|
|
|
case CheckStructureImmediate:
|
|
|
|
case GetMyArgumentByVal:
|
|
|
|
case GetMyArgumentByValOutOfBounds:
|
2020-08-29 13:27:11 +00:00
|
|
|
case GetPrototypeOf:
|
2017-08-12 16:48:01 +00:00
|
|
|
case GetRegExpObjectLastIndex:
|
|
|
|
case MapHash:
|
2020-08-29 13:27:11 +00:00
|
|
|
case NormalizeMapKey:
|
|
|
|
case StringSlice:
|
2017-08-12 16:48:01 +00:00
|
|
|
case ToLowerCase:
|
|
|
|
case GetMapBucket:
|
2020-08-29 13:27:11 +00:00
|
|
|
case GetMapBucketHead:
|
|
|
|
case GetMapBucketNext:
|
|
|
|
case LoadKeyFromMapBucket:
|
|
|
|
case LoadValueFromMapBucket:
|
|
|
|
case ExtractValueFromWeakMapGet:
|
|
|
|
case WeakMapGet:
|
2018-01-03 05:16:05 +00:00
|
|
|
case AtomicsIsLockFree:
|
2020-08-29 13:27:11 +00:00
|
|
|
case MatchStructure:
|
2022-10-23 02:55:20 +00:00
|
|
|
case DateGetInt32OrNaN:
|
|
|
|
case DateGetTime:
|
2020-08-29 13:27:11 +00:00
|
|
|
case DataViewGetInt:
|
|
|
|
case DataViewGetFloat:
|
2017-08-12 16:48:01 +00:00
|
|
|
return true;
|
|
|
|
|
2022-10-23 02:55:20 +00:00
|
|
|
case GetButterfly:
|
|
|
|
return state.forNode(node->child1()).isType(SpecObject);
|
|
|
|
|
2018-01-03 05:16:05 +00:00
|
|
|
case ArraySlice:
|
|
|
|
case ArrayIndexOf: {
|
2017-08-12 16:48:01 +00:00
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
|
2022-10-23 02:55:20 +00:00
|
|
|
case GetGetter:
|
|
|
|
case GetSetter: {
|
|
|
|
if (!state.forNode(node->child1()).isType(SpecCell))
|
|
|
|
return false;
|
|
|
|
StructureAbstractValue& value = state.forNode(node->child1()).m_structure;
|
|
|
|
if (value.isInfinite() || value.size() != 1)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return value[0].get() == graph.m_vm.getterSetterStructure;
|
|
|
|
}
|
|
|
|
|
2017-08-12 16:48:01 +00:00
|
|
|
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;
|
2020-08-29 13:27:11 +00:00
|
|
|
|
|
|
|
case FilterCallLinkStatus:
|
2022-10-23 02:55:20 +00:00
|
|
|
case FilterGetByStatus:
|
2020-08-29 13:27:11 +00:00
|
|
|
case FilterPutByIdStatus:
|
|
|
|
case FilterInByIdStatus:
|
2022-10-23 02:55:20 +00:00
|
|
|
case FilterDeleteByStatus:
|
2020-08-29 13:27:11 +00:00
|
|
|
// We don't want these to be moved anywhere other than where we put them, since we want them
|
|
|
|
// to capture "profiling" at the point in control flow here the user put them.
|
|
|
|
return false;
|
2017-08-12 16:48:01 +00:00
|
|
|
|
|
|
|
case GetByVal:
|
|
|
|
case GetIndexedPropertyStorage:
|
|
|
|
case GetArrayLength:
|
2018-01-03 05:16:05 +00:00
|
|
|
case GetVectorLength:
|
2017-08-12 16:48:01 +00:00
|
|
|
case ArrayPop:
|
|
|
|
case StringCharAt:
|
|
|
|
case StringCharCodeAt:
|
2022-10-23 02:55:20 +00:00
|
|
|
case StringCodePointAt:
|
2020-08-29 13:27:11 +00:00
|
|
|
return node->arrayMode().alreadyChecked(graph, node, state.forNode(graph.child(node, 0)));
|
|
|
|
|
|
|
|
case ArrayPush:
|
|
|
|
return node->arrayMode().alreadyChecked(graph, node, state.forNode(graph.varArgChild(node, 1)));
|
|
|
|
|
2022-10-23 02:55:20 +00:00
|
|
|
case CheckDetached:
|
2017-08-12 16:48:01 +00:00
|
|
|
case GetTypedArrayByteOffset:
|
|
|
|
return !(state.forNode(node->child1()).m_type & ~(SpecTypedArrayView));
|
|
|
|
|
|
|
|
case PutByValDirect:
|
|
|
|
case PutByVal:
|
|
|
|
case PutByValAlias:
|
|
|
|
return node->arrayMode().modeForPut().alreadyChecked(
|
|
|
|
graph, node, state.forNode(graph.varArgChild(node, 0)));
|
|
|
|
|
|
|
|
case AllocatePropertyStorage:
|
|
|
|
case ReallocatePropertyStorage:
|
|
|
|
return state.forNode(node->child1()).m_structure.isSubsetOf(
|
|
|
|
RegisteredStructureSet(node->transition()->previous));
|
|
|
|
|
2022-10-23 02:55:20 +00:00
|
|
|
case GetGetterSetterByOffset: {
|
|
|
|
// If it's an inline property, we need to make sure it's a cell before trusting what the structure set tells us.
|
|
|
|
if (node->child1().node() == node->child2().node() && !state.forNode(node->child2()).isType(SpecCell))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
StorageAccessData& data = node->storageAccessData();
|
|
|
|
auto* uid = graph.identifiers()[data.identifierNumber];
|
|
|
|
PropertyOffset desiredOffset = data.offset;
|
|
|
|
StructureAbstractValue& value = state.forNode(node->child2()).m_structure;
|
|
|
|
if (value.isInfinite())
|
|
|
|
return false;
|
|
|
|
for (unsigned i = value.size(); i--;) {
|
|
|
|
Structure* thisStructure = value[i].get();
|
|
|
|
if (thisStructure->isUncacheableDictionary())
|
|
|
|
return false;
|
|
|
|
unsigned attributes = 0;
|
|
|
|
PropertyOffset checkOffset = thisStructure->getConcurrently(uid, attributes);
|
|
|
|
if (checkOffset != desiredOffset || !(attributes & PropertyAttribute::Accessor))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-08-12 16:48:01 +00:00
|
|
|
case GetByOffset:
|
|
|
|
case PutByOffset: {
|
2022-10-23 02:55:20 +00:00
|
|
|
// If it's an inline property, we need to make sure it's a cell before trusting what the structure set tells us.
|
|
|
|
if (node->child1().node() == node->child2().node() && !state.forNode(node->child2()).isType(SpecCell))
|
|
|
|
return false;
|
|
|
|
|
2020-08-29 13:27:11 +00:00
|
|
|
StorageAccessData& data = node->storageAccessData();
|
|
|
|
PropertyOffset offset = data.offset;
|
|
|
|
// Graph::isSafeToLoad() is all about proofs derived from PropertyConditions. Those don't
|
|
|
|
// know anything about inferred types. But if we have a proof derived from watching a
|
|
|
|
// structure that has a type proof, then the next case below will deal with it.
|
2017-08-12 16:48:01 +00:00
|
|
|
if (state.structureClobberState() == StructuresAreWatched) {
|
2020-08-29 13:27:11 +00:00
|
|
|
if (JSObject* knownBase = node->child2()->dynamicCastConstant<JSObject*>(graph.m_vm)) {
|
2017-08-12 16:48:01 +00:00
|
|
|
if (graph.isSafeToLoad(knownBase, offset))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-29 13:27:11 +00:00
|
|
|
StructureAbstractValue& value = state.forNode(node->child2()).m_structure;
|
2017-08-12 16:48:01 +00:00
|
|
|
if (value.isInfinite())
|
|
|
|
return false;
|
|
|
|
for (unsigned i = value.size(); i--;) {
|
2020-08-29 13:27:11 +00:00
|
|
|
Structure* thisStructure = value[i].get();
|
2022-10-23 02:55:20 +00:00
|
|
|
if (thisStructure->isUncacheableDictionary())
|
|
|
|
return false;
|
2020-08-29 13:27:11 +00:00
|
|
|
if (!thisStructure->isValidOffset(offset))
|
2017-08-12 16:48:01 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2022-10-23 02:55:20 +00:00
|
|
|
case CallDOMGetter:
|
|
|
|
case CallDOM: {
|
|
|
|
Node* thisNode = node->child1().node();
|
|
|
|
StructureAbstractValue& structures = state.forNode(thisNode).m_structure;
|
|
|
|
if (!structures.isFinite())
|
|
|
|
return false;
|
|
|
|
bool isSafe = true;
|
|
|
|
const ClassInfo* classInfo = node->requiredDOMJITClassInfo();
|
|
|
|
structures.forEach([&] (RegisteredStructure structure) {
|
|
|
|
isSafe &= structure->classInfo()->isSubClassOf(classInfo);
|
|
|
|
});
|
|
|
|
return isSafe;
|
|
|
|
}
|
2020-08-29 13:27:11 +00:00
|
|
|
|
2022-10-23 02:55:20 +00:00
|
|
|
case ToThis:
|
|
|
|
case CreateThis:
|
|
|
|
case CreatePromise:
|
|
|
|
case CreateGenerator:
|
|
|
|
case CreateAsyncGenerator:
|
|
|
|
case ObjectCreate:
|
|
|
|
case ObjectKeys:
|
|
|
|
case ObjectGetOwnPropertyNames:
|
|
|
|
case SetLocal:
|
|
|
|
case SetCallee:
|
|
|
|
case PutStack:
|
|
|
|
case KillStack:
|
|
|
|
case MovHint:
|
|
|
|
case Upsilon:
|
|
|
|
case Phi:
|
|
|
|
case Flush:
|
|
|
|
case SetArgumentDefinitely:
|
|
|
|
case SetArgumentMaybe:
|
|
|
|
case SetArgumentCountIncludingThis:
|
|
|
|
case PhantomLocal:
|
|
|
|
case DeleteById:
|
|
|
|
case DeleteByVal:
|
|
|
|
case GetById:
|
|
|
|
case GetByIdWithThis:
|
|
|
|
case GetByValWithThis:
|
|
|
|
case GetByIdFlush:
|
|
|
|
case GetByIdDirect:
|
|
|
|
case GetByIdDirectFlush:
|
|
|
|
case PutById:
|
|
|
|
case PutByIdFlush:
|
|
|
|
case PutByIdWithThis:
|
|
|
|
case PutByValWithThis:
|
|
|
|
case PutByIdDirect:
|
|
|
|
case PutGetterById:
|
|
|
|
case PutSetterById:
|
|
|
|
case PutGetterSetterById:
|
|
|
|
case PutGetterByVal:
|
|
|
|
case PutSetterByVal:
|
|
|
|
case PutPrivateName:
|
|
|
|
case PutPrivateNameById:
|
|
|
|
case GetPrivateName:
|
|
|
|
case GetPrivateNameById:
|
|
|
|
case DefineDataProperty:
|
|
|
|
case DefineAccessorProperty:
|
|
|
|
case Arrayify:
|
|
|
|
case ArrayifyToStructure:
|
|
|
|
case PutClosureVar:
|
|
|
|
case PutGlobalVariable:
|
|
|
|
case CheckBadValue:
|
|
|
|
case RegExpExec:
|
|
|
|
case RegExpExecNonGlobalOrSticky:
|
|
|
|
case RegExpTest:
|
|
|
|
case RegExpMatchFast:
|
|
|
|
case RegExpMatchFastGlobal:
|
|
|
|
case Call:
|
|
|
|
case DirectCall:
|
|
|
|
case TailCallInlinedCaller:
|
|
|
|
case DirectTailCallInlinedCaller:
|
|
|
|
case Construct:
|
|
|
|
case DirectConstruct:
|
|
|
|
case CallVarargs:
|
|
|
|
case CallEval:
|
|
|
|
case TailCallVarargsInlinedCaller:
|
|
|
|
case TailCallForwardVarargsInlinedCaller:
|
|
|
|
case ConstructVarargs:
|
|
|
|
case VarargsLength:
|
|
|
|
case LoadVarargs:
|
|
|
|
case CallForwardVarargs:
|
|
|
|
case ConstructForwardVarargs:
|
|
|
|
case NewObject:
|
|
|
|
case NewGenerator:
|
|
|
|
case NewAsyncGenerator:
|
|
|
|
case NewArray:
|
|
|
|
case NewArrayWithSize:
|
|
|
|
case NewArrayBuffer:
|
|
|
|
case NewArrayWithSpread:
|
|
|
|
case NewInternalFieldObject:
|
|
|
|
case Spread:
|
|
|
|
case NewRegexp:
|
|
|
|
case NewSymbol:
|
|
|
|
case ProfileType:
|
|
|
|
case ProfileControlFlow:
|
|
|
|
case InstanceOf:
|
|
|
|
case InstanceOfCustom:
|
|
|
|
case CallObjectConstructor:
|
|
|
|
case ToPrimitive:
|
|
|
|
case ToPropertyKey:
|
|
|
|
case ToNumber:
|
|
|
|
case ToNumeric:
|
|
|
|
case ToObject:
|
|
|
|
case CallNumberConstructor:
|
|
|
|
case NumberToStringWithRadix:
|
|
|
|
case SetFunctionName:
|
|
|
|
case NewStringObject:
|
|
|
|
case InByVal:
|
|
|
|
case InById:
|
|
|
|
case HasOwnProperty:
|
|
|
|
case PushWithScope:
|
|
|
|
case CreateActivation:
|
|
|
|
case CreateDirectArguments:
|
|
|
|
case CreateScopedArguments:
|
|
|
|
case CreateClonedArguments:
|
|
|
|
case CreateArgumentsButterfly:
|
|
|
|
case PutToArguments:
|
|
|
|
case NewFunction:
|
|
|
|
case NewGeneratorFunction:
|
|
|
|
case NewAsyncGeneratorFunction:
|
|
|
|
case NewAsyncFunction:
|
|
|
|
case Jump:
|
|
|
|
case Branch:
|
|
|
|
case Switch:
|
|
|
|
case EntrySwitch:
|
|
|
|
case Return:
|
|
|
|
case TailCall:
|
|
|
|
case DirectTailCall:
|
|
|
|
case TailCallVarargs:
|
|
|
|
case TailCallForwardVarargs:
|
|
|
|
case Throw:
|
|
|
|
case ThrowStaticError:
|
|
|
|
case CountExecution:
|
|
|
|
case SuperSamplerBegin:
|
|
|
|
case SuperSamplerEnd:
|
|
|
|
case ForceOSRExit:
|
|
|
|
case CPUIntrinsic:
|
|
|
|
case CheckTraps:
|
|
|
|
case LogShadowChickenPrologue:
|
|
|
|
case LogShadowChickenTail:
|
|
|
|
case NewTypedArray:
|
|
|
|
case Unreachable:
|
|
|
|
case ClearCatchLocals:
|
|
|
|
case CheckTierUpInLoop:
|
|
|
|
case CheckTierUpAtReturn:
|
|
|
|
case CheckTierUpAndOSREnter:
|
|
|
|
case LoopHint:
|
|
|
|
case InvalidationPoint:
|
|
|
|
case NotifyWrite:
|
|
|
|
case MultiPutByOffset:
|
|
|
|
case MultiDeleteByOffset:
|
|
|
|
case GetEnumerableLength:
|
|
|
|
case HasEnumerableStructureProperty:
|
|
|
|
case HasEnumerableProperty:
|
|
|
|
case HasOwnStructureProperty:
|
|
|
|
case InStructureProperty:
|
|
|
|
case GetDirectPname:
|
|
|
|
case GetPropertyEnumerator:
|
|
|
|
case PhantomNewObject:
|
|
|
|
case PhantomNewFunction:
|
|
|
|
case PhantomNewGeneratorFunction:
|
|
|
|
case PhantomNewAsyncGeneratorFunction:
|
|
|
|
case PhantomNewAsyncFunction:
|
|
|
|
case PhantomNewInternalFieldObject:
|
|
|
|
case PhantomCreateActivation:
|
|
|
|
case PhantomNewRegexp:
|
|
|
|
case PutHint:
|
|
|
|
case MaterializeNewObject:
|
|
|
|
case MaterializeCreateActivation:
|
|
|
|
case MaterializeNewInternalFieldObject:
|
|
|
|
case PhantomDirectArguments:
|
|
|
|
case PhantomCreateRest:
|
|
|
|
case PhantomSpread:
|
|
|
|
case PhantomNewArrayWithSpread:
|
|
|
|
case PhantomNewArrayBuffer:
|
|
|
|
case PhantomClonedArguments:
|
|
|
|
case ForwardVarargs:
|
|
|
|
case CreateRest:
|
|
|
|
case SetRegExpObjectLastIndex:
|
|
|
|
case RecordRegExpCachedResult:
|
|
|
|
case GetDynamicVar:
|
|
|
|
case PutDynamicVar:
|
|
|
|
case ResolveScopeForHoistingFuncDeclInEval:
|
|
|
|
case ResolveScope:
|
|
|
|
case StringValueOf:
|
|
|
|
case WeakSetAdd:
|
|
|
|
case WeakMapSet:
|
|
|
|
case AtomicsAdd:
|
|
|
|
case AtomicsAnd:
|
|
|
|
case AtomicsCompareExchange:
|
|
|
|
case AtomicsExchange:
|
|
|
|
case AtomicsLoad:
|
|
|
|
case AtomicsOr:
|
|
|
|
case AtomicsStore:
|
|
|
|
case AtomicsSub:
|
|
|
|
case AtomicsXor:
|
|
|
|
case InitializeEntrypointArguments:
|
|
|
|
case ValueNegate:
|
|
|
|
case GetInternalField:
|
|
|
|
case PutInternalField:
|
|
|
|
case DataViewSet:
|
2020-08-29 13:27:11 +00:00
|
|
|
case SetAdd:
|
|
|
|
case MapSet:
|
2022-10-23 02:55:20 +00:00
|
|
|
case StringReplaceRegExp:
|
|
|
|
case StringReplace:
|
|
|
|
case ArithRandom:
|
|
|
|
case ArithIMul:
|
|
|
|
case TryGetById:
|
2020-08-29 13:27:11 +00:00
|
|
|
return false;
|
|
|
|
|
2022-10-23 02:55:20 +00:00
|
|
|
case Inc:
|
|
|
|
case Dec:
|
|
|
|
return node->child1().useKind() != UntypedUse;
|
|
|
|
|
|
|
|
case ValueBitAnd:
|
|
|
|
case ValueBitXor:
|
|
|
|
case ValueBitOr:
|
|
|
|
case ValueBitLShift:
|
|
|
|
case ValueBitRShift:
|
|
|
|
case ValueAdd:
|
|
|
|
case ValueSub:
|
|
|
|
case ValueMul:
|
|
|
|
case ValueDiv:
|
|
|
|
case ValueMod:
|
|
|
|
case ValuePow:
|
|
|
|
return node->isBinaryUseKind(AnyBigIntUse) || node->isBinaryUseKind(BigInt32Use) || node->isBinaryUseKind(HeapBigIntUse);
|
|
|
|
|
|
|
|
case ValueBitNot:
|
|
|
|
return node->child1().useKind() == AnyBigIntUse || node->child1().useKind() == BigInt32Use || node->child1().useKind() == HeapBigIntUse;
|
|
|
|
|
2017-08-12 16:48:01 +00:00
|
|
|
case LastNodeType:
|
|
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
} } // namespace JSC::DFG
|
|
|
|
|
|
|
|
#endif // ENABLE(DFG_JIT)
|