1/*
2 * Copyright (C) 2013-2017 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 "FTLCapabilities.h"
28
29#if ENABLE(FTL_JIT)
30
31namespace JSC { namespace FTL {
32
33using namespace DFG;
34
35static bool verboseCapabilities()
36{
37 return verboseCompilationEnabled() || Options::verboseFTLFailure();
38}
39
40inline CapabilityLevel canCompile(Node* node)
41{
42 // NOTE: If we ever have phantom arguments, we can compile them but we cannot
43 // OSR enter.
44
45 switch (node->op()) {
46 case JSConstant:
47 case LazyJSConstant:
48 case GetLocal:
49 case SetLocal:
50 case PutStack:
51 case KillStack:
52 case GetStack:
53 case MovHint:
54 case ZombieHint:
55 case ExitOK:
56 case Phantom:
57 case Flush:
58 case PhantomLocal:
59 case SetArgument:
60 case Return:
61 case ArithBitNot:
62 case ArithBitAnd:
63 case ArithBitOr:
64 case ArithBitXor:
65 case BitRShift:
66 case BitLShift:
67 case BitURShift:
68 case CheckStructure:
69 case CheckStructureOrEmpty:
70 case DoubleAsInt32:
71 case Arrayify:
72 case ArrayifyToStructure:
73 case PutStructure:
74 case GetButterfly:
75 case NewObject:
76 case NewStringObject:
77 case NewSymbol:
78 case NewArray:
79 case NewArrayWithSpread:
80 case Spread:
81 case NewArrayBuffer:
82 case NewTypedArray:
83 case GetByOffset:
84 case GetGetterSetterByOffset:
85 case GetGetter:
86 case GetSetter:
87 case PutByOffset:
88 case GetGlobalVar:
89 case GetGlobalLexicalVariable:
90 case PutGlobalVariable:
91 case ValueBitAnd:
92 case ValueBitXor:
93 case ValueBitOr:
94 case ValueBitNot:
95 case ValueNegate:
96 case ValueAdd:
97 case ValueSub:
98 case ValueMul:
99 case ValueDiv:
100 case StrCat:
101 case ArithAdd:
102 case ArithClz32:
103 case ArithSub:
104 case ArithMul:
105 case ArithDiv:
106 case ArithMod:
107 case ArithMin:
108 case ArithMax:
109 case ArithAbs:
110 case ArithPow:
111 case ArithRandom:
112 case ArithRound:
113 case ArithFloor:
114 case ArithCeil:
115 case ArithTrunc:
116 case ArithSqrt:
117 case ArithFRound:
118 case ArithNegate:
119 case ArithUnary:
120 case UInt32ToNumber:
121 case Jump:
122 case ForceOSRExit:
123 case Phi:
124 case Upsilon:
125 case ExtractOSREntryLocal:
126 case ExtractCatchLocal:
127 case ClearCatchLocals:
128 case LoopHint:
129 case SkipScope:
130 case GetGlobalObject:
131 case GetGlobalThis:
132 case CreateActivation:
133 case PushWithScope:
134 case NewFunction:
135 case NewGeneratorFunction:
136 case NewAsyncFunction:
137 case NewAsyncGeneratorFunction:
138 case GetClosureVar:
139 case PutClosureVar:
140 case CreateDirectArguments:
141 case CreateScopedArguments:
142 case CreateClonedArguments:
143 case GetFromArguments:
144 case PutToArguments:
145 case GetArgument:
146 case InvalidationPoint:
147 case StringCharAt:
148 case CheckCell:
149 case CheckBadCell:
150 case CheckNotEmpty:
151 case AssertNotEmpty:
152 case CheckStringIdent:
153 case CheckTraps:
154 case StringCharCodeAt:
155 case StringFromCharCode:
156 case AllocatePropertyStorage:
157 case ReallocatePropertyStorage:
158 case NukeStructureAndSetButterfly:
159 case GetTypedArrayByteOffset:
160 case GetPrototypeOf:
161 case NotifyWrite:
162 case StoreBarrier:
163 case FencedStoreBarrier:
164 case Call:
165 case DirectCall:
166 case TailCall:
167 case DirectTailCall:
168 case TailCallInlinedCaller:
169 case DirectTailCallInlinedCaller:
170 case Construct:
171 case DirectConstruct:
172 case CallVarargs:
173 case CallEval:
174 case TailCallVarargs:
175 case TailCallVarargsInlinedCaller:
176 case ConstructVarargs:
177 case CallForwardVarargs:
178 case TailCallForwardVarargs:
179 case TailCallForwardVarargsInlinedCaller:
180 case ConstructForwardVarargs:
181 case LoadVarargs:
182 case ValueToInt32:
183 case Branch:
184 case LogicalNot:
185 case CheckInBounds:
186 case ConstantStoragePointer:
187 case Check:
188 case CheckVarargs:
189 case CheckArray:
190 case CountExecution:
191 case SuperSamplerBegin:
192 case SuperSamplerEnd:
193 case GetExecutable:
194 case GetScope:
195 case GetCallee:
196 case SetCallee:
197 case GetArgumentCountIncludingThis:
198 case SetArgumentCountIncludingThis:
199 case ToNumber:
200 case ToString:
201 case ToObject:
202 case CallObjectConstructor:
203 case CallStringConstructor:
204 case ObjectCreate:
205 case ObjectKeys:
206 case MakeRope:
207 case NewArrayWithSize:
208 case TryGetById:
209 case GetById:
210 case GetByIdFlush:
211 case GetByIdWithThis:
212 case GetByIdDirect:
213 case GetByIdDirectFlush:
214 case ToThis:
215 case MultiGetByOffset:
216 case MultiPutByOffset:
217 case ToPrimitive:
218 case Throw:
219 case ThrowStaticError:
220 case Unreachable:
221 case InByVal:
222 case InById:
223 case HasOwnProperty:
224 case IsCellWithType:
225 case MapHash:
226 case NormalizeMapKey:
227 case GetMapBucket:
228 case GetMapBucketHead:
229 case GetMapBucketNext:
230 case LoadKeyFromMapBucket:
231 case LoadValueFromMapBucket:
232 case ExtractValueFromWeakMapGet:
233 case SetAdd:
234 case MapSet:
235 case WeakMapGet:
236 case WeakSetAdd:
237 case WeakMapSet:
238 case IsEmpty:
239 case IsUndefined:
240 case IsUndefinedOrNull:
241 case IsBoolean:
242 case IsNumber:
243 case NumberIsInteger:
244 case IsObject:
245 case IsObjectOrNull:
246 case IsFunction:
247 case IsTypedArrayView:
248 case CheckTypeInfoFlags:
249 case OverridesHasInstance:
250 case InstanceOf:
251 case InstanceOfCustom:
252 case DoubleRep:
253 case ValueRep:
254 case Int52Rep:
255 case DoubleConstant:
256 case Int52Constant:
257 case BooleanToNumber:
258 case HasGenericProperty:
259 case HasStructureProperty:
260 case HasIndexedProperty:
261 case GetDirectPname:
262 case GetEnumerableLength:
263 case GetIndexedPropertyStorage:
264 case GetPropertyEnumerator:
265 case GetEnumeratorStructurePname:
266 case GetEnumeratorGenericPname:
267 case ToIndexString:
268 case BottomValue:
269 case PhantomNewObject:
270 case PhantomNewFunction:
271 case PhantomNewGeneratorFunction:
272 case PhantomNewAsyncGeneratorFunction:
273 case PhantomNewAsyncFunction:
274 case PhantomCreateActivation:
275 case PhantomNewRegexp:
276 case PutHint:
277 case CheckStructureImmediate:
278 case MaterializeNewObject:
279 case MaterializeCreateActivation:
280 case PhantomDirectArguments:
281 case PhantomCreateRest:
282 case PhantomSpread:
283 case PhantomNewArrayWithSpread:
284 case PhantomNewArrayBuffer:
285 case PhantomClonedArguments:
286 case GetMyArgumentByVal:
287 case GetMyArgumentByValOutOfBounds:
288 case ForwardVarargs:
289 case EntrySwitch:
290 case Switch:
291 case TypeOf:
292 case PutById:
293 case PutByIdDirect:
294 case PutByIdFlush:
295 case PutByIdWithThis:
296 case PutGetterById:
297 case PutSetterById:
298 case PutGetterSetterById:
299 case PutGetterByVal:
300 case PutSetterByVal:
301 case DeleteById:
302 case DeleteByVal:
303 case CreateRest:
304 case GetRestLength:
305 case RegExpExec:
306 case RegExpExecNonGlobalOrSticky:
307 case RegExpTest:
308 case RegExpMatchFast:
309 case RegExpMatchFastGlobal:
310 case NewRegexp:
311 case StringReplace:
312 case StringReplaceRegExp:
313 case GetRegExpObjectLastIndex:
314 case SetRegExpObjectLastIndex:
315 case RecordRegExpCachedResult:
316 case SetFunctionName:
317 case LogShadowChickenPrologue:
318 case LogShadowChickenTail:
319 case ResolveScope:
320 case ResolveScopeForHoistingFuncDeclInEval:
321 case GetDynamicVar:
322 case PutDynamicVar:
323 case CompareEq:
324 case CompareEqPtr:
325 case CompareLess:
326 case CompareLessEq:
327 case CompareGreater:
328 case CompareGreaterEq:
329 case CompareBelow:
330 case CompareBelowEq:
331 case CompareStrictEq:
332 case SameValue:
333 case DefineDataProperty:
334 case DefineAccessorProperty:
335 case StringValueOf:
336 case StringSlice:
337 case ToLowerCase:
338 case NumberToStringWithRadix:
339 case NumberToStringWithValidRadixConstant:
340 case CheckSubClass:
341 case CallDOM:
342 case CallDOMGetter:
343 case ArraySlice:
344 case ArrayIndexOf:
345 case ArrayPop:
346 case ArrayPush:
347 case ParseInt:
348 case AtomicsAdd:
349 case AtomicsAnd:
350 case AtomicsCompareExchange:
351 case AtomicsExchange:
352 case AtomicsLoad:
353 case AtomicsOr:
354 case AtomicsStore:
355 case AtomicsSub:
356 case AtomicsXor:
357 case AtomicsIsLockFree:
358 case InitializeEntrypointArguments:
359 case CPUIntrinsic:
360 case GetArrayLength:
361 case GetVectorLength:
362 case GetByVal:
363 case GetByValWithThis:
364 case PutByVal:
365 case PutByValAlias:
366 case PutByValDirect:
367 case PutByValWithThis:
368 case MatchStructure:
369 case FilterCallLinkStatus:
370 case FilterGetByIdStatus:
371 case FilterPutByIdStatus:
372 case FilterInByIdStatus:
373 case CreateThis:
374 case DataViewGetInt:
375 case DataViewGetFloat:
376 case DataViewSet:
377 // These are OK.
378 break;
379
380 case Identity:
381 // No backend handles this because it will be optimized out. But we may check
382 // for capabilities before optimization. It would be a deep error to remove this
383 // case because it would prevent us from catching bugs where the FTL backend
384 // pipeline failed to optimize out an Identity.
385 break;
386
387 case IdentityWithProfile:
388 case CheckTierUpInLoop:
389 case CheckTierUpAndOSREnter:
390 case CheckTierUpAtReturn:
391 case FiatInt52:
392 case ArithIMul:
393 case ProfileType:
394 case ProfileControlFlow:
395 case LastNodeType:
396 return CannotCompile;
397 }
398 return CanCompileAndOSREnter;
399}
400
401CapabilityLevel canCompile(Graph& graph)
402{
403 if (graph.m_codeBlock->instructionCount() > Options::maximumFTLCandidateInstructionCount()) {
404 if (verboseCapabilities())
405 dataLog("FTL rejecting ", *graph.m_codeBlock, " because it's too big.\n");
406 return CannotCompile;
407 }
408
409 if (UNLIKELY(graph.m_codeBlock->ownerExecutable()->neverFTLOptimize())) {
410 if (verboseCapabilities())
411 dataLog("FTL rejecting ", *graph.m_codeBlock, " because it is marked as never FTL compile.\n");
412 return CannotCompile;
413 }
414
415 CapabilityLevel result = CanCompileAndOSREnter;
416
417 for (BlockIndex blockIndex = graph.numBlocks(); blockIndex--;) {
418 BasicBlock* block = graph.block(blockIndex);
419 if (!block)
420 continue;
421
422 // We don't care if we can compile blocks that the CFA hasn't visited.
423 if (!block->cfaHasVisited)
424 continue;
425
426 for (unsigned nodeIndex = 0; nodeIndex < block->size(); ++nodeIndex) {
427 Node* node = block->at(nodeIndex);
428
429 for (unsigned childIndex = graph.numChildren(node); childIndex--;) {
430 Edge edge = graph.child(node, childIndex);
431 if (!edge)
432 continue;
433 switch (edge.useKind()) {
434 case UntypedUse:
435 case Int32Use:
436 case KnownInt32Use:
437 case Int52RepUse:
438 case NumberUse:
439 case RealNumberUse:
440 case DoubleRepUse:
441 case DoubleRepRealUse:
442 case BooleanUse:
443 case KnownBooleanUse:
444 case CellUse:
445 case KnownCellUse:
446 case CellOrOtherUse:
447 case ObjectUse:
448 case ArrayUse:
449 case FunctionUse:
450 case ObjectOrOtherUse:
451 case StringUse:
452 case StringOrOtherUse:
453 case KnownStringUse:
454 case KnownPrimitiveUse:
455 case StringObjectUse:
456 case StringOrStringObjectUse:
457 case SymbolUse:
458 case BigIntUse:
459 case MapObjectUse:
460 case SetObjectUse:
461 case WeakMapObjectUse:
462 case WeakSetObjectUse:
463 case DataViewObjectUse:
464 case FinalObjectUse:
465 case RegExpObjectUse:
466 case ProxyObjectUse:
467 case DerivedArrayUse:
468 case NotCellUse:
469 case OtherUse:
470 case KnownOtherUse:
471 case MiscUse:
472 case StringIdentUse:
473 case NotStringVarUse:
474 case NotSymbolUse:
475 case AnyIntUse:
476 case DoubleRepAnyIntUse:
477 // These are OK.
478 break;
479 default:
480 // Don't know how to handle anything else.
481 if (verboseCapabilities()) {
482 dataLog("FTL rejecting node in ", *graph.m_codeBlock, " because of bad use kind: ", edge.useKind(), " in node:\n");
483 graph.dump(WTF::dataFile(), " ", node);
484 }
485 return CannotCompile;
486 }
487 }
488
489 switch (canCompile(node)) {
490 case CannotCompile:
491 if (verboseCapabilities()) {
492 dataLog("FTL rejecting node in ", *graph.m_codeBlock, ":\n");
493 graph.dump(WTF::dataFile(), " ", node);
494 }
495 return CannotCompile;
496
497 case CanCompile:
498 if (result == CanCompileAndOSREnter && verboseCompilationEnabled()) {
499 dataLog("FTL disabling OSR entry because of node:\n");
500 graph.dump(WTF::dataFile(), " ", node);
501 }
502 result = CanCompile;
503 break;
504
505 case CanCompileAndOSREnter:
506 break;
507 }
508
509 if (node->op() == ForceOSRExit)
510 break;
511 }
512 }
513
514 return result;
515}
516
517} } // namespace JSC::FTL
518
519#endif // ENABLE(FTL_JIT)
520
521