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