mirror of
https://github.com/darlinghq/darling-JavaScriptCore.git
synced 2025-04-07 09:21:41 +00:00
589 lines
17 KiB
C++
589 lines
17 KiB
C++
/*
|
|
* Copyright (C) 2014-2020 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.
|
|
*/
|
|
|
|
#include "config.h"
|
|
#include "DFGDoesGC.h"
|
|
|
|
#if ENABLE(DFG_JIT)
|
|
|
|
#include "DFGClobberize.h"
|
|
#include "DFGGraph.h"
|
|
#include "DFGNode.h"
|
|
|
|
namespace JSC { namespace DFG {
|
|
|
|
bool doesGC(Graph& graph, Node* node)
|
|
{
|
|
if (clobbersHeap(graph, node))
|
|
return true;
|
|
|
|
// Now consider nodes that don't clobber the world but that still may GC. This includes all
|
|
// nodes. By default, we should assume every node can GC and return true. This includes the
|
|
// world-clobbering nodes. We should only return false if we have proven that the node cannot
|
|
// GC. Typical examples of how a node can GC is if the code emitted for the node does any of the
|
|
// following:
|
|
// 1. Allocates any objects.
|
|
// 2. Resolves a rope string, which allocates a string.
|
|
// 3. Produces a string (which allocates the string) except when we can prove that
|
|
// the string will always be one of the pre-allcoated SmallStrings.
|
|
// 4. Triggers a structure transition (which can allocate a new structure)
|
|
// unless it is a known transition between previously allocated structures
|
|
// such as between Array types.
|
|
// 5. Calls to a JS function, which can execute arbitrary code including allocating objects.
|
|
// 6. Calls operations that uses DeferGC, because it may GC in its destructor.
|
|
|
|
switch (node->op()) {
|
|
case JSConstant:
|
|
case DoubleConstant:
|
|
case Int52Constant:
|
|
case LazyJSConstant:
|
|
case Identity:
|
|
case IdentityWithProfile:
|
|
case GetCallee:
|
|
case SetCallee:
|
|
case GetArgumentCountIncludingThis:
|
|
case SetArgumentCountIncludingThis:
|
|
case GetRestLength:
|
|
case GetLocal:
|
|
case SetLocal:
|
|
case MovHint:
|
|
case InitializeEntrypointArguments:
|
|
case ExitOK:
|
|
case Phantom:
|
|
case Upsilon:
|
|
case Phi:
|
|
case Flush:
|
|
case PhantomLocal:
|
|
case SetArgumentDefinitely:
|
|
case SetArgumentMaybe:
|
|
case ArithBitNot:
|
|
case ArithBitAnd:
|
|
case ArithBitOr:
|
|
case ArithBitXor:
|
|
case ArithBitLShift:
|
|
case ArithBitRShift:
|
|
case BitURShift:
|
|
case ValueToInt32:
|
|
case UInt32ToNumber:
|
|
case DoubleAsInt32:
|
|
case ArithAdd:
|
|
case ArithClz32:
|
|
case ArithSub:
|
|
case ArithNegate:
|
|
case ArithMul:
|
|
case ArithIMul:
|
|
case ArithDiv:
|
|
case ArithMod:
|
|
case ArithAbs:
|
|
case ArithMin:
|
|
case ArithMax:
|
|
case ArithPow:
|
|
case ArithSqrt:
|
|
case ArithRandom:
|
|
case ArithRound:
|
|
case ArithFloor:
|
|
case ArithCeil:
|
|
case ArithTrunc:
|
|
case ArithFRound:
|
|
case ArithUnary:
|
|
case CheckStructure:
|
|
case CheckStructureOrEmpty:
|
|
case CheckStructureImmediate:
|
|
case GetExecutable:
|
|
case GetButterfly:
|
|
case CheckJSCast:
|
|
case CheckNotJSCast:
|
|
case CheckArray:
|
|
case CheckArrayOrEmpty:
|
|
case CheckDetached:
|
|
case GetScope:
|
|
case SkipScope:
|
|
case GetGlobalObject:
|
|
case GetGlobalThis:
|
|
case GetClosureVar:
|
|
case PutClosureVar:
|
|
case GetInternalField:
|
|
case PutInternalField:
|
|
case GetRegExpObjectLastIndex:
|
|
case SetRegExpObjectLastIndex:
|
|
case RecordRegExpCachedResult:
|
|
case GetGlobalVar:
|
|
case GetGlobalLexicalVariable:
|
|
case PutGlobalVariable:
|
|
case CheckIsConstant:
|
|
case CheckNotEmpty:
|
|
case AssertNotEmpty:
|
|
case CheckIdent:
|
|
case CompareBelow:
|
|
case CompareBelowEq:
|
|
case CompareEqPtr:
|
|
case ProfileControlFlow:
|
|
case OverridesHasInstance:
|
|
case IsEmpty:
|
|
case TypeOfIsUndefined:
|
|
case TypeOfIsObject:
|
|
case TypeOfIsFunction:
|
|
case IsUndefinedOrNull:
|
|
case IsBoolean:
|
|
case IsNumber:
|
|
case IsBigInt:
|
|
case NumberIsInteger:
|
|
case IsObject:
|
|
case IsCallable:
|
|
case IsConstructor:
|
|
case IsCellWithType:
|
|
case IsTypedArrayView:
|
|
case TypeOf:
|
|
case LogicalNot:
|
|
case Jump:
|
|
case Branch:
|
|
case EntrySwitch:
|
|
case CountExecution:
|
|
case SuperSamplerBegin:
|
|
case SuperSamplerEnd:
|
|
case CPUIntrinsic:
|
|
case NormalizeMapKey: // HeapBigInt => BigInt32 conversion does not involve GC.
|
|
case GetMapBucketHead:
|
|
case GetMapBucketNext:
|
|
case LoadKeyFromMapBucket:
|
|
case LoadValueFromMapBucket:
|
|
case ExtractValueFromWeakMapGet:
|
|
case WeakMapGet:
|
|
case WeakSetAdd:
|
|
case WeakMapSet:
|
|
case Unreachable:
|
|
case ExtractOSREntryLocal:
|
|
case ExtractCatchLocal:
|
|
case ClearCatchLocals:
|
|
case LoopHint:
|
|
case StoreBarrier:
|
|
case FencedStoreBarrier:
|
|
case InvalidationPoint:
|
|
case NotifyWrite:
|
|
case AssertInBounds:
|
|
case CheckInBounds:
|
|
case ConstantStoragePointer:
|
|
case Check:
|
|
case CheckVarargs:
|
|
case CheckTypeInfoFlags:
|
|
case MultiGetByOffset:
|
|
case MultiDeleteByOffset:
|
|
case ValueRep:
|
|
case DoubleRep:
|
|
case Int52Rep:
|
|
case GetGetter:
|
|
case GetSetter:
|
|
case GetArrayLength:
|
|
case GetVectorLength:
|
|
case StringCharCodeAt:
|
|
case StringCodePointAt:
|
|
case GetTypedArrayByteOffset:
|
|
case GetPrototypeOf:
|
|
case PutStructure:
|
|
case GetByOffset:
|
|
case GetGetterSetterByOffset:
|
|
case GetEnumerableLength:
|
|
case FiatInt52:
|
|
case BooleanToNumber:
|
|
case CheckBadValue:
|
|
case BottomValue:
|
|
case PhantomNewObject:
|
|
case PhantomNewFunction:
|
|
case PhantomNewGeneratorFunction:
|
|
case PhantomNewAsyncFunction:
|
|
case PhantomNewAsyncGeneratorFunction:
|
|
case PhantomNewInternalFieldObject:
|
|
case PhantomCreateActivation:
|
|
case PhantomDirectArguments:
|
|
case PhantomCreateRest:
|
|
case PhantomNewArrayWithSpread:
|
|
case PhantomNewArrayBuffer:
|
|
case PhantomSpread:
|
|
case PhantomClonedArguments:
|
|
case PhantomNewRegexp:
|
|
case GetMyArgumentByVal:
|
|
case GetMyArgumentByValOutOfBounds:
|
|
case ForwardVarargs:
|
|
case PutHint:
|
|
case KillStack:
|
|
case GetStack:
|
|
case GetFromArguments:
|
|
case GetArgument:
|
|
case LogShadowChickenPrologue:
|
|
case LogShadowChickenTail:
|
|
case NukeStructureAndSetButterfly:
|
|
case AtomicsAdd:
|
|
case AtomicsAnd:
|
|
case AtomicsCompareExchange:
|
|
case AtomicsExchange:
|
|
case AtomicsLoad:
|
|
case AtomicsOr:
|
|
case AtomicsStore:
|
|
case AtomicsSub:
|
|
case AtomicsXor:
|
|
case AtomicsIsLockFree:
|
|
case MatchStructure:
|
|
case FilterCallLinkStatus:
|
|
case FilterGetByStatus:
|
|
case FilterPutByIdStatus:
|
|
case FilterInByIdStatus:
|
|
case FilterDeleteByStatus:
|
|
case DateGetInt32OrNaN:
|
|
case DateGetTime:
|
|
case DataViewGetInt:
|
|
case DataViewGetFloat:
|
|
case DataViewSet:
|
|
return false;
|
|
|
|
#if ASSERT_ENABLED
|
|
case ArrayPush:
|
|
case ArrayPop:
|
|
case PushWithScope:
|
|
case CreateActivation:
|
|
case CreateDirectArguments:
|
|
case CreateScopedArguments:
|
|
case CreateClonedArguments:
|
|
case CreateArgumentsButterfly:
|
|
case Call:
|
|
case CallEval:
|
|
case CallForwardVarargs:
|
|
case CallObjectConstructor:
|
|
case CallVarargs:
|
|
case CheckTierUpAndOSREnter:
|
|
case CheckTierUpAtReturn:
|
|
case CheckTierUpInLoop:
|
|
case Construct:
|
|
case ConstructForwardVarargs:
|
|
case ConstructVarargs:
|
|
case DefineDataProperty:
|
|
case DefineAccessorProperty:
|
|
case DeleteById:
|
|
case DeleteByVal:
|
|
case DirectCall:
|
|
case DirectConstruct:
|
|
case DirectTailCall:
|
|
case DirectTailCallInlinedCaller:
|
|
case ForceOSRExit:
|
|
case GetById:
|
|
case GetByIdDirect:
|
|
case GetByIdDirectFlush:
|
|
case GetByIdFlush:
|
|
case GetByIdWithThis:
|
|
case GetByValWithThis:
|
|
case GetDirectPname:
|
|
case GetDynamicVar:
|
|
case GetMapBucket:
|
|
case HasIndexedProperty:
|
|
case HasEnumerableIndexedProperty:
|
|
case HasEnumerableStructureProperty:
|
|
case HasEnumerableProperty:
|
|
case HasOwnProperty:
|
|
case HasOwnStructureProperty:
|
|
case InStructureProperty:
|
|
case InById:
|
|
case InByVal:
|
|
case InstanceOf:
|
|
case InstanceOfCustom:
|
|
case VarargsLength:
|
|
case LoadVarargs:
|
|
case NumberToStringWithRadix:
|
|
case NumberToStringWithValidRadixConstant:
|
|
case ProfileType:
|
|
case PutById:
|
|
case PutByIdDirect:
|
|
case PutByIdFlush:
|
|
case PutByIdWithThis:
|
|
case PutByOffset:
|
|
case PutByValWithThis:
|
|
case PutDynamicVar:
|
|
case PutGetterById:
|
|
case PutGetterByVal:
|
|
case PutGetterSetterById:
|
|
case PutSetterById:
|
|
case PutSetterByVal:
|
|
case PutPrivateName:
|
|
case PutPrivateNameById:
|
|
case GetPrivateName:
|
|
case GetPrivateNameById:
|
|
case PutStack:
|
|
case PutToArguments:
|
|
case RegExpExec:
|
|
case RegExpExecNonGlobalOrSticky:
|
|
case RegExpMatchFast:
|
|
case RegExpMatchFastGlobal:
|
|
case RegExpTest:
|
|
case ResolveScope:
|
|
case ResolveScopeForHoistingFuncDeclInEval:
|
|
case Return:
|
|
case StringCharAt:
|
|
case TailCall:
|
|
case TailCallForwardVarargs:
|
|
case TailCallForwardVarargsInlinedCaller:
|
|
case TailCallInlinedCaller:
|
|
case TailCallVarargs:
|
|
case TailCallVarargsInlinedCaller:
|
|
case Throw:
|
|
case ToNumber:
|
|
case ToNumeric:
|
|
case ToObject:
|
|
case ToPrimitive:
|
|
case ToPropertyKey:
|
|
case ToThis:
|
|
case TryGetById:
|
|
case CreateThis:
|
|
case CreatePromise:
|
|
case CreateGenerator:
|
|
case CreateAsyncGenerator:
|
|
case ObjectCreate:
|
|
case ObjectKeys:
|
|
case ObjectGetOwnPropertyNames:
|
|
case AllocatePropertyStorage:
|
|
case ReallocatePropertyStorage:
|
|
case Arrayify:
|
|
case ArrayifyToStructure:
|
|
case NewObject:
|
|
case NewGenerator:
|
|
case NewAsyncGenerator:
|
|
case NewArray:
|
|
case NewArrayWithSpread:
|
|
case NewInternalFieldObject:
|
|
case Spread:
|
|
case NewArrayWithSize:
|
|
case NewArrayBuffer:
|
|
case NewRegexp:
|
|
case NewStringObject:
|
|
case NewSymbol:
|
|
case MakeRope:
|
|
case NewFunction:
|
|
case NewGeneratorFunction:
|
|
case NewAsyncGeneratorFunction:
|
|
case NewAsyncFunction:
|
|
case NewTypedArray:
|
|
case ThrowStaticError:
|
|
case GetPropertyEnumerator:
|
|
case GetEnumeratorStructurePname:
|
|
case GetEnumeratorGenericPname:
|
|
case ToIndexString:
|
|
case MaterializeNewObject:
|
|
case MaterializeNewInternalFieldObject:
|
|
case MaterializeCreateActivation:
|
|
case SetFunctionName:
|
|
case StrCat:
|
|
case StringReplace:
|
|
case StringReplaceRegExp:
|
|
case StringSlice:
|
|
case StringValueOf:
|
|
case CreateRest:
|
|
case ToLowerCase:
|
|
case CallDOMGetter:
|
|
case CallDOM:
|
|
case ArraySlice:
|
|
case ArrayIndexOf:
|
|
case ParseInt: // We might resolve a rope even though we don't clobber anything.
|
|
case SetAdd:
|
|
case MapSet:
|
|
case ValueBitAnd:
|
|
case ValueBitOr:
|
|
case ValueBitXor:
|
|
case ValueBitLShift:
|
|
case ValueBitRShift:
|
|
case ValueAdd:
|
|
case ValueSub:
|
|
case ValueMul:
|
|
case ValueDiv:
|
|
case ValueMod:
|
|
case ValuePow:
|
|
case ValueBitNot:
|
|
case ValueNegate:
|
|
#else // not ASSERT_ENABLED
|
|
// See comment at the top for why the default for all nodes should be to
|
|
// return true.
|
|
default:
|
|
#endif // not ASSERT_ENABLED
|
|
return true;
|
|
|
|
case CallNumberConstructor:
|
|
switch (node->child1().useKind()) {
|
|
case BigInt32Use:
|
|
return false;
|
|
default:
|
|
break;
|
|
}
|
|
return true;
|
|
|
|
case CallStringConstructor:
|
|
case ToString:
|
|
switch (node->child1().useKind()) {
|
|
case StringObjectUse:
|
|
case StringOrStringObjectUse:
|
|
return false;
|
|
default:
|
|
break;
|
|
}
|
|
return true;
|
|
|
|
case CheckTraps:
|
|
// FIXME: https://bugs.webkit.org/show_bug.cgi?id=194323
|
|
ASSERT(Options::usePollingTraps());
|
|
return true;
|
|
|
|
case CompareEq:
|
|
case CompareLess:
|
|
case CompareLessEq:
|
|
case CompareGreater:
|
|
case CompareGreaterEq:
|
|
// FIXME: Add AnyBigIntUse and HeapBigIntUse specific optimizations in DFG / FTL code generation and ensure it does not perform GC.
|
|
// https://bugs.webkit.org/show_bug.cgi?id=210923
|
|
if (node->isBinaryUseKind(Int32Use)
|
|
#if USE(JSVALUE64)
|
|
|| node->isBinaryUseKind(Int52RepUse)
|
|
#endif
|
|
|| node->isBinaryUseKind(DoubleRepUse)
|
|
|| node->isBinaryUseKind(BigInt32Use)
|
|
|| node->isBinaryUseKind(StringIdentUse)
|
|
)
|
|
return false;
|
|
if (node->op() == CompareEq) {
|
|
if (node->isBinaryUseKind(BooleanUse)
|
|
|| node->isBinaryUseKind(SymbolUse)
|
|
|| node->isBinaryUseKind(ObjectUse)
|
|
|| node->isBinaryUseKind(ObjectUse, ObjectOrOtherUse) || node->isBinaryUseKind(ObjectOrOtherUse, ObjectUse))
|
|
return false;
|
|
}
|
|
return true;
|
|
|
|
case CompareStrictEq:
|
|
if (node->isBinaryUseKind(BooleanUse)
|
|
|| node->isBinaryUseKind(Int32Use)
|
|
#if USE(JSVALUE64)
|
|
|| node->isBinaryUseKind(Int52RepUse)
|
|
#endif
|
|
|| node->isBinaryUseKind(DoubleRepUse)
|
|
|| node->isBinaryUseKind(SymbolUse)
|
|
|| node->isBinaryUseKind(SymbolUse, UntypedUse)
|
|
|| node->isBinaryUseKind(UntypedUse, SymbolUse)
|
|
|| node->isBinaryUseKind(StringIdentUse)
|
|
|| node->isBinaryUseKind(ObjectUse, UntypedUse) || node->isBinaryUseKind(UntypedUse, ObjectUse)
|
|
|| node->isBinaryUseKind(ObjectUse)
|
|
|| node->isBinaryUseKind(MiscUse, UntypedUse) || node->isBinaryUseKind(UntypedUse, MiscUse)
|
|
|| node->isBinaryUseKind(StringIdentUse, NotStringVarUse) || node->isBinaryUseKind(NotStringVarUse, StringIdentUse))
|
|
return false;
|
|
return true;
|
|
|
|
case GetIndexedPropertyStorage:
|
|
case GetByVal:
|
|
if (node->arrayMode().type() == Array::String)
|
|
return true;
|
|
return false;
|
|
|
|
case PutByValDirect:
|
|
case PutByVal:
|
|
case PutByValAlias:
|
|
if (!graph.m_plan.isFTL()) {
|
|
switch (node->arrayMode().modeForPut().type()) {
|
|
case Array::Int8Array:
|
|
case Array::Int16Array:
|
|
case Array::Int32Array:
|
|
case Array::Uint8Array:
|
|
case Array::Uint8ClampedArray:
|
|
case Array::Uint16Array:
|
|
case Array::Uint32Array:
|
|
return true;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
return false;
|
|
|
|
case MapHash:
|
|
switch (node->child1().useKind()) {
|
|
case BooleanUse:
|
|
case Int32Use:
|
|
case SymbolUse:
|
|
case ObjectUse:
|
|
#if USE(BIGINT32)
|
|
case BigInt32Use:
|
|
#endif
|
|
case HeapBigIntUse:
|
|
return false;
|
|
default:
|
|
// We might resolve a rope.
|
|
return true;
|
|
}
|
|
|
|
case MultiPutByOffset:
|
|
return node->multiPutByOffsetData().reallocatesStorage();
|
|
|
|
case SameValue:
|
|
if (node->isBinaryUseKind(DoubleRepUse))
|
|
return false;
|
|
return true;
|
|
|
|
case StringFromCharCode:
|
|
// FIXME: Should we constant fold this case?
|
|
// https://bugs.webkit.org/show_bug.cgi?id=194308
|
|
if (node->child1()->isInt32Constant() && (node->child1()->asUInt32() <= maxSingleCharacterString))
|
|
return false;
|
|
return true;
|
|
|
|
case Switch:
|
|
switch (node->switchData()->kind) {
|
|
case SwitchCell:
|
|
ASSERT(graph.m_plan.isFTL());
|
|
FALLTHROUGH;
|
|
case SwitchImm:
|
|
return false;
|
|
case SwitchChar:
|
|
return true;
|
|
case SwitchString:
|
|
if (node->child1().useKind() == StringIdentUse)
|
|
return false;
|
|
ASSERT(node->child1().useKind() == StringUse || node->child1().useKind() == UntypedUse);
|
|
return true;
|
|
}
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
|
|
case Inc:
|
|
case Dec:
|
|
switch (node->child1().useKind()) {
|
|
case Int32Use:
|
|
case Int52RepUse:
|
|
case DoubleRepUse:
|
|
return false;
|
|
default:
|
|
return true;
|
|
}
|
|
|
|
case LastNodeType:
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
}
|
|
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
}
|
|
|
|
} } // namespace JSC::DFG
|
|
|
|
#endif // ENABLE(DFG_JIT)
|