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