1/*
2 * Copyright (C) 2014-2019 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "DFGDoesGC.h"
28
29#if ENABLE(DFG_JIT)
30
31#include "DFGClobberize.h"
32#include "DFGGraph.h"
33#include "DFGNode.h"
34#include "Operations.h"
35
36namespace JSC { namespace DFG {
37
38bool doesGC(Graph& graph, Node* node)
39{
40 if (clobbersHeap(graph, node))
41 return true;
42
43 // Now consider nodes that don't clobber the world but that still may GC. This includes all
44 // nodes. By default, we should assume every node can GC and return true. This includes the
45 // world-clobbering nodes. We should only return false if we have proven that the node cannot
46 // GC. Typical examples of how a node can GC is if the code emitted for the node does any of the
47 // following:
48 // 1. Allocates any objects.
49 // 2. Resolves a rope string, which allocates a string.
50 // 3. Produces a string (which allocates the string) except when we can prove that
51 // the string will always be one of the pre-allcoated SmallStrings.
52 // 4. Triggers a structure transition (which can allocate a new structure)
53 // unless it is a known transition between previously allocated structures
54 // such as between Array types.
55 // 5. Calls to a JS function, which can execute arbitrary code including allocating objects.
56 // 6. Calls operations that uses DeferGC, because it may GC in its destructor.
57
58 switch (node->op()) {
59 case JSConstant:
60 case DoubleConstant:
61 case Int52Constant:
62 case LazyJSConstant:
63 case Identity:
64 case IdentityWithProfile:
65 case GetCallee:
66 case SetCallee:
67 case GetArgumentCountIncludingThis:
68 case SetArgumentCountIncludingThis:
69 case GetRestLength:
70 case GetLocal:
71 case SetLocal:
72 case MovHint:
73 case InitializeEntrypointArguments:
74 case ZombieHint:
75 case ExitOK:
76 case Phantom:
77 case Upsilon:
78 case Phi:
79 case Flush:
80 case PhantomLocal:
81 case SetArgumentDefinitely:
82 case SetArgumentMaybe:
83 case ArithBitNot:
84 case ArithBitAnd:
85 case ArithBitOr:
86 case ArithBitXor:
87 case ArithBitLShift:
88 case ArithBitRShift:
89 case BitURShift:
90 case ValueToInt32:
91 case UInt32ToNumber:
92 case DoubleAsInt32:
93 case ArithAdd:
94 case ArithClz32:
95 case ArithSub:
96 case ArithNegate:
97 case ArithMul:
98 case ArithIMul:
99 case ArithDiv:
100 case ArithMod:
101 case ArithAbs:
102 case ArithMin:
103 case ArithMax:
104 case ArithPow:
105 case ArithSqrt:
106 case ArithRandom:
107 case ArithRound:
108 case ArithFloor:
109 case ArithCeil:
110 case ArithTrunc:
111 case ArithFRound:
112 case ArithUnary:
113 case CheckStructure:
114 case CheckStructureOrEmpty:
115 case CheckStructureImmediate:
116 case GetExecutable:
117 case GetButterfly:
118 case CheckSubClass:
119 case CheckArray:
120 case GetScope:
121 case SkipScope:
122 case GetGlobalObject:
123 case GetGlobalThis:
124 case GetClosureVar:
125 case PutClosureVar:
126 case GetInternalField:
127 case PutInternalField:
128 case GetRegExpObjectLastIndex:
129 case SetRegExpObjectLastIndex:
130 case RecordRegExpCachedResult:
131 case GetGlobalVar:
132 case GetGlobalLexicalVariable:
133 case PutGlobalVariable:
134 case CheckCell:
135 case CheckNotEmpty:
136 case AssertNotEmpty:
137 case CheckIdent:
138 case CompareBelow:
139 case CompareBelowEq:
140 case CompareEqPtr:
141 case ProfileControlFlow:
142 case OverridesHasInstance:
143 case IsEmpty:
144 case IsUndefined:
145 case IsUndefinedOrNull:
146 case IsBoolean:
147 case IsNumber:
148 case NumberIsInteger:
149 case IsObject:
150 case IsObjectOrNull:
151 case IsFunction:
152 case IsCellWithType:
153 case IsTypedArrayView:
154 case TypeOf:
155 case LogicalNot:
156 case Jump:
157 case Branch:
158 case EntrySwitch:
159 case CountExecution:
160 case SuperSamplerBegin:
161 case SuperSamplerEnd:
162 case CPUIntrinsic:
163 case NormalizeMapKey:
164 case GetMapBucketHead:
165 case GetMapBucketNext:
166 case LoadKeyFromMapBucket:
167 case LoadValueFromMapBucket:
168 case ExtractValueFromWeakMapGet:
169 case WeakMapGet:
170 case WeakSetAdd:
171 case WeakMapSet:
172 case Unreachable:
173 case ExtractOSREntryLocal:
174 case ExtractCatchLocal:
175 case ClearCatchLocals:
176 case LoopHint:
177 case StoreBarrier:
178 case FencedStoreBarrier:
179 case InvalidationPoint:
180 case NotifyWrite:
181 case CheckInBounds:
182 case ConstantStoragePointer:
183 case Check:
184 case CheckVarargs:
185 case CheckTypeInfoFlags:
186 case MultiGetByOffset:
187 case ValueRep:
188 case DoubleRep:
189 case Int52Rep:
190 case GetGetter:
191 case GetSetter:
192 case GetArrayLength:
193 case GetVectorLength:
194 case StringCharCodeAt:
195 case StringCodePointAt:
196 case GetTypedArrayByteOffset:
197 case GetPrototypeOf:
198 case PutStructure:
199 case GetByOffset:
200 case GetGetterSetterByOffset:
201 case GetEnumerableLength:
202 case FiatInt52:
203 case BooleanToNumber:
204 case CheckBadCell:
205 case BottomValue:
206 case PhantomNewObject:
207 case PhantomNewFunction:
208 case PhantomNewGeneratorFunction:
209 case PhantomNewAsyncFunction:
210 case PhantomNewAsyncGeneratorFunction:
211 case PhantomCreateActivation:
212 case PhantomDirectArguments:
213 case PhantomCreateRest:
214 case PhantomNewArrayWithSpread:
215 case PhantomNewArrayBuffer:
216 case PhantomSpread:
217 case PhantomClonedArguments:
218 case PhantomNewRegexp:
219 case GetMyArgumentByVal:
220 case GetMyArgumentByValOutOfBounds:
221 case ForwardVarargs:
222 case PutHint:
223 case KillStack:
224 case GetStack:
225 case GetFromArguments:
226 case GetArgument:
227 case LogShadowChickenPrologue:
228 case LogShadowChickenTail:
229 case NukeStructureAndSetButterfly:
230 case AtomicsAdd:
231 case AtomicsAnd:
232 case AtomicsCompareExchange:
233 case AtomicsExchange:
234 case AtomicsLoad:
235 case AtomicsOr:
236 case AtomicsStore:
237 case AtomicsSub:
238 case AtomicsXor:
239 case AtomicsIsLockFree:
240 case MatchStructure:
241 case FilterCallLinkStatus:
242 case FilterGetByStatus:
243 case FilterPutByIdStatus:
244 case FilterInByIdStatus:
245 case DateGetInt32OrNaN:
246 case DateGetTime:
247 case DataViewGetInt:
248 case DataViewGetFloat:
249 case DataViewSet:
250 return false;
251
252#if !ASSERT_DISABLED
253 case ArrayPush:
254 case ArrayPop:
255 case PushWithScope:
256 case CreateActivation:
257 case CreateDirectArguments:
258 case CreateScopedArguments:
259 case CreateClonedArguments:
260 case Call:
261 case CallEval:
262 case CallForwardVarargs:
263 case CallObjectConstructor:
264 case CallVarargs:
265 case CheckTierUpAndOSREnter:
266 case CheckTierUpAtReturn:
267 case CheckTierUpInLoop:
268 case Construct:
269 case ConstructForwardVarargs:
270 case ConstructVarargs:
271 case DefineDataProperty:
272 case DefineAccessorProperty:
273 case DeleteById:
274 case DeleteByVal:
275 case DirectCall:
276 case DirectConstruct:
277 case DirectTailCall:
278 case DirectTailCallInlinedCaller:
279 case ForceOSRExit:
280 case GetById:
281 case GetByIdDirect:
282 case GetByIdDirectFlush:
283 case GetByIdFlush:
284 case GetByIdWithThis:
285 case GetByValWithThis:
286 case GetDirectPname:
287 case GetDynamicVar:
288 case GetMapBucket:
289 case HasGenericProperty:
290 case HasIndexedProperty:
291 case HasOwnProperty:
292 case HasStructureProperty:
293 case InById:
294 case InByVal:
295 case InstanceOf:
296 case InstanceOfCustom:
297 case LoadVarargs:
298 case NumberToStringWithRadix:
299 case NumberToStringWithValidRadixConstant:
300 case ProfileType:
301 case PutById:
302 case PutByIdDirect:
303 case PutByIdFlush:
304 case PutByIdWithThis:
305 case PutByOffset:
306 case PutByValWithThis:
307 case PutDynamicVar:
308 case PutGetterById:
309 case PutGetterByVal:
310 case PutGetterSetterById:
311 case PutSetterById:
312 case PutSetterByVal:
313 case PutStack:
314 case PutToArguments:
315 case RegExpExec:
316 case RegExpExecNonGlobalOrSticky:
317 case RegExpMatchFast:
318 case RegExpMatchFastGlobal:
319 case RegExpTest:
320 case ResolveScope:
321 case ResolveScopeForHoistingFuncDeclInEval:
322 case Return:
323 case StringCharAt:
324 case TailCall:
325 case TailCallForwardVarargs:
326 case TailCallForwardVarargsInlinedCaller:
327 case TailCallInlinedCaller:
328 case TailCallVarargs:
329 case TailCallVarargsInlinedCaller:
330 case Throw:
331 case ToNumber:
332 case ToNumeric:
333 case ToObject:
334 case ToPrimitive:
335 case ToThis:
336 case TryGetById:
337 case CreateThis:
338 case CreatePromise:
339 case CreateGenerator:
340 case CreateAsyncGenerator:
341 case ObjectCreate:
342 case ObjectKeys:
343 case AllocatePropertyStorage:
344 case ReallocatePropertyStorage:
345 case Arrayify:
346 case ArrayifyToStructure:
347 case NewObject:
348 case NewPromise:
349 case NewGenerator:
350 case NewAsyncGenerator:
351 case NewArray:
352 case NewArrayWithSpread:
353 case Spread:
354 case NewArrayWithSize:
355 case NewArrayBuffer:
356 case NewRegexp:
357 case NewStringObject:
358 case NewSymbol:
359 case MakeRope:
360 case NewFunction:
361 case NewGeneratorFunction:
362 case NewAsyncGeneratorFunction:
363 case NewAsyncFunction:
364 case NewTypedArray:
365 case ThrowStaticError:
366 case GetPropertyEnumerator:
367 case GetEnumeratorStructurePname:
368 case GetEnumeratorGenericPname:
369 case ToIndexString:
370 case MaterializeNewObject:
371 case MaterializeCreateActivation:
372 case SetFunctionName:
373 case StrCat:
374 case StringReplace:
375 case StringReplaceRegExp:
376 case StringSlice:
377 case StringValueOf:
378 case CreateRest:
379 case ToLowerCase:
380 case CallDOMGetter:
381 case CallDOM:
382 case ArraySlice:
383 case ArrayIndexOf:
384 case ParseInt: // We might resolve a rope even though we don't clobber anything.
385 case SetAdd:
386 case MapSet:
387 case ValueBitAnd:
388 case ValueBitOr:
389 case ValueBitXor:
390 case ValueBitLShift:
391 case ValueBitRShift:
392 case ValueAdd:
393 case ValueSub:
394 case ValueMul:
395 case ValueDiv:
396 case ValueMod:
397 case ValuePow:
398 case ValueBitNot:
399 case ValueNegate:
400#else
401 // See comment at the top for why be default for all nodes should be to
402 // return true.
403 default:
404#endif
405 return true;
406
407 case CallStringConstructor:
408 case ToString:
409 switch (node->child1().useKind()) {
410 case StringObjectUse:
411 case StringOrStringObjectUse:
412 return false;
413 default:
414 break;
415 }
416 return true;
417
418 case CheckTraps:
419 // FIXME: https://bugs.webkit.org/show_bug.cgi?id=194323
420 ASSERT(Options::usePollingTraps());
421 return true;
422
423 case CompareEq:
424 case CompareLess:
425 case CompareLessEq:
426 case CompareGreater:
427 case CompareGreaterEq:
428 if (node->isBinaryUseKind(Int32Use)
429#if USE(JSVALUE64)
430 || node->isBinaryUseKind(Int52RepUse)
431#endif
432 || node->isBinaryUseKind(DoubleRepUse)
433 || node->isBinaryUseKind(StringIdentUse)
434 )
435 return false;
436 if (node->op() == CompareEq) {
437 if (node->isBinaryUseKind(BooleanUse)
438 || node->isBinaryUseKind(SymbolUse)
439 || node->isBinaryUseKind(ObjectUse)
440 || node->isBinaryUseKind(ObjectUse, ObjectOrOtherUse) || node->isBinaryUseKind(ObjectOrOtherUse, ObjectUse))
441 return false;
442 }
443 return true;
444
445 case CompareStrictEq:
446 if (node->isBinaryUseKind(BooleanUse)
447 || node->isBinaryUseKind(Int32Use)
448#if USE(JSVALUE64)
449 || node->isBinaryUseKind(Int52RepUse)
450#endif
451 || node->isBinaryUseKind(DoubleRepUse)
452 || node->isBinaryUseKind(SymbolUse)
453 || node->isBinaryUseKind(SymbolUse, UntypedUse)
454 || node->isBinaryUseKind(UntypedUse, SymbolUse)
455 || node->isBinaryUseKind(StringIdentUse)
456 || node->isBinaryUseKind(ObjectUse, UntypedUse) || node->isBinaryUseKind(UntypedUse, ObjectUse)
457 || node->isBinaryUseKind(ObjectUse)
458 || node->isBinaryUseKind(MiscUse, UntypedUse) || node->isBinaryUseKind(UntypedUse, MiscUse)
459 || node->isBinaryUseKind(StringIdentUse, NotStringVarUse) || node->isBinaryUseKind(NotStringVarUse, StringIdentUse))
460 return false;
461 return true;
462
463 case GetIndexedPropertyStorage:
464 case GetByVal:
465 if (node->arrayMode().type() == Array::String)
466 return true;
467 return false;
468
469 case PutByValDirect:
470 case PutByVal:
471 case PutByValAlias:
472 if (!graph.m_plan.isFTL()) {
473 switch (node->arrayMode().modeForPut().type()) {
474 case Array::Int8Array:
475 case Array::Int16Array:
476 case Array::Int32Array:
477 case Array::Uint8Array:
478 case Array::Uint8ClampedArray:
479 case Array::Uint16Array:
480 case Array::Uint32Array:
481 return true;
482 default:
483 break;
484 }
485 }
486 return false;
487
488 case MapHash:
489 switch (node->child1().useKind()) {
490 case BooleanUse:
491 case Int32Use:
492 case SymbolUse:
493 case ObjectUse:
494 return false;
495 default:
496 // We might resolve a rope.
497 return true;
498 }
499
500 case MultiPutByOffset:
501 return node->multiPutByOffsetData().reallocatesStorage();
502
503 case SameValue:
504 if (node->isBinaryUseKind(DoubleRepUse))
505 return false;
506 return true;
507
508 case StringFromCharCode:
509 // FIXME: Should we constant fold this case?
510 // https://bugs.webkit.org/show_bug.cgi?id=194308
511 if (node->child1()->isInt32Constant() && (node->child1()->asUInt32() <= maxSingleCharacterString))
512 return false;
513 return true;
514
515 case Switch:
516 switch (node->switchData()->kind) {
517 case SwitchCell:
518 ASSERT(graph.m_plan.isFTL());
519 FALLTHROUGH;
520 case SwitchImm:
521 return false;
522 case SwitchChar:
523 return true;
524 case SwitchString:
525 if (node->child1().useKind() == StringIdentUse)
526 return false;
527 ASSERT(node->child1().useKind() == StringUse || node->child1().useKind() == UntypedUse);
528 return true;
529 }
530 RELEASE_ASSERT_NOT_REACHED();
531
532 case Inc:
533 case Dec:
534 switch (node->child1().useKind()) {
535 case Int32Use:
536 case Int52RepUse:
537 case DoubleRepUse:
538 return false;
539 default:
540 return true;
541 }
542
543 case LastNodeType:
544 RELEASE_ASSERT_NOT_REACHED();
545 }
546
547 RELEASE_ASSERT_NOT_REACHED();
548}
549
550} } // namespace JSC::DFG
551
552#endif // ENABLE(DFG_JIT)
553