Source code

Revision control

Other Tools

1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2
* vim: set ts=8 sts=2 et sw=2 tw=80:
3
* This Source Code Form is subject to the terms of the Mozilla Public
4
* License, v. 2.0. If a copy of the MPL was not distributed with this
5
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "mozilla/Casting.h"
8
9
#include "jsmath.h"
10
11
#include "builtin/AtomicsObject.h"
12
#ifdef JS_HAS_INTL_API
13
# include "builtin/intl/Collator.h"
14
# include "builtin/intl/DateTimeFormat.h"
15
# include "builtin/intl/ListFormat.h"
16
# include "builtin/intl/NumberFormat.h"
17
# include "builtin/intl/PluralRules.h"
18
# include "builtin/intl/RelativeTimeFormat.h"
19
#endif
20
#include "builtin/MapObject.h"
21
#include "builtin/String.h"
22
#include "builtin/TestingFunctions.h"
23
#include "builtin/TypedObject.h"
24
#include "jit/BaselineInspector.h"
25
#include "jit/InlinableNatives.h"
26
#include "jit/IonBuilder.h"
27
#include "jit/Lowering.h"
28
#include "jit/MIR.h"
29
#include "jit/MIRGraph.h"
30
#include "js/RegExpFlags.h" // JS::RegExpFlag, JS::RegExpFlags
31
#include "vm/ArgumentsObject.h"
32
#include "vm/ArrayBufferObject.h"
33
#include "vm/JSObject.h"
34
#include "vm/ProxyObject.h"
35
#include "vm/SelfHosting.h"
36
#include "vm/SharedArrayObject.h"
37
#include "vm/TypedArrayObject.h"
38
#include "wasm/WasmInstance.h"
39
40
#include "jit/shared/Lowering-shared-inl.h"
41
#include "vm/JSScript-inl.h"
42
#include "vm/NativeObject-inl.h"
43
#include "vm/StringObject-inl.h"
44
45
using mozilla::ArrayLength;
46
using mozilla::AssertedCast;
47
using mozilla::Maybe;
48
49
using JS::RegExpFlag;
50
using JS::RegExpFlags;
51
52
namespace js {
53
namespace jit {
54
55
// Returns true if |native| can be inlined cross-realm. Especially inlined
56
// natives that can allocate objects or throw exceptions shouldn't be inlined
57
// cross-realm without a careful analysis because we might use the wrong realm!
58
//
59
// Note that self-hosting intrinsics are never called cross-realm. See the
60
// MOZ_CRASH below.
61
//
62
// If you are adding a new inlinable native, the safe thing is to |return false|
63
// here.
64
static bool CanInlineCrossRealm(InlinableNative native) {
65
switch (native) {
66
case InlinableNative::MathAbs:
67
case InlinableNative::MathFloor:
68
case InlinableNative::MathCeil:
69
case InlinableNative::MathRound:
70
case InlinableNative::MathClz32:
71
case InlinableNative::MathSqrt:
72
case InlinableNative::MathATan2:
73
case InlinableNative::MathHypot:
74
case InlinableNative::MathMax:
75
case InlinableNative::MathMin:
76
case InlinableNative::MathPow:
77
case InlinableNative::MathImul:
78
case InlinableNative::MathFRound:
79
case InlinableNative::MathTrunc:
80
case InlinableNative::MathSign:
81
case InlinableNative::MathSin:
82
case InlinableNative::MathTan:
83
case InlinableNative::MathCos:
84
case InlinableNative::MathExp:
85
case InlinableNative::MathLog:
86
case InlinableNative::MathASin:
87
case InlinableNative::MathATan:
88
case InlinableNative::MathACos:
89
case InlinableNative::MathLog10:
90
case InlinableNative::MathLog2:
91
case InlinableNative::MathLog1P:
92
case InlinableNative::MathExpM1:
93
case InlinableNative::MathCosH:
94
case InlinableNative::MathSinH:
95
case InlinableNative::MathTanH:
96
case InlinableNative::MathACosH:
97
case InlinableNative::MathASinH:
98
case InlinableNative::MathATanH:
99
case InlinableNative::MathCbrt:
100
case InlinableNative::Boolean:
101
return true;
102
103
case InlinableNative::Array:
104
// Cross-realm case handled by inlineArray.
105
return true;
106
107
case InlinableNative::MathRandom:
108
// RNG state is per-realm.
109
return false;
110
111
case InlinableNative::IntlGuardToCollator:
112
case InlinableNative::IntlGuardToDateTimeFormat:
113
case InlinableNative::IntlGuardToListFormat:
114
case InlinableNative::IntlGuardToNumberFormat:
115
case InlinableNative::IntlGuardToPluralRules:
116
case InlinableNative::IntlGuardToRelativeTimeFormat:
117
case InlinableNative::IsRegExpObject:
118
case InlinableNative::IsPossiblyWrappedRegExpObject:
119
case InlinableNative::RegExpMatcher:
120
case InlinableNative::RegExpSearcher:
121
case InlinableNative::RegExpTester:
122
case InlinableNative::RegExpPrototypeOptimizable:
123
case InlinableNative::RegExpInstanceOptimizable:
124
case InlinableNative::GetFirstDollarIndex:
125
case InlinableNative::IntrinsicNewArrayIterator:
126
case InlinableNative::IntrinsicNewStringIterator:
127
case InlinableNative::IntrinsicNewRegExpStringIterator:
128
case InlinableNative::IntrinsicStringReplaceString:
129
case InlinableNative::IntrinsicStringSplitString:
130
case InlinableNative::IntrinsicUnsafeSetReservedSlot:
131
case InlinableNative::IntrinsicUnsafeGetReservedSlot:
132
case InlinableNative::IntrinsicUnsafeGetObjectFromReservedSlot:
133
case InlinableNative::IntrinsicUnsafeGetInt32FromReservedSlot:
134
case InlinableNative::IntrinsicUnsafeGetStringFromReservedSlot:
135
case InlinableNative::IntrinsicUnsafeGetBooleanFromReservedSlot:
136
case InlinableNative::IntrinsicIsCallable:
137
case InlinableNative::IntrinsicIsConstructor:
138
case InlinableNative::IntrinsicToObject:
139
case InlinableNative::IntrinsicIsObject:
140
case InlinableNative::IntrinsicIsCrossRealmArrayConstructor:
141
case InlinableNative::IntrinsicToInteger:
142
case InlinableNative::IntrinsicToString:
143
case InlinableNative::IntrinsicIsConstructing:
144
case InlinableNative::IntrinsicSubstringKernel:
145
case InlinableNative::IntrinsicGuardToArrayIterator:
146
case InlinableNative::IntrinsicGuardToMapIterator:
147
case InlinableNative::IntrinsicGuardToSetIterator:
148
case InlinableNative::IntrinsicGuardToStringIterator:
149
case InlinableNative::IntrinsicGuardToRegExpStringIterator:
150
case InlinableNative::IntrinsicObjectHasPrototype:
151
case InlinableNative::IntrinsicFinishBoundFunctionInit:
152
case InlinableNative::IntrinsicIsPackedArray:
153
case InlinableNative::IntrinsicGuardToMapObject:
154
case InlinableNative::IntrinsicGetNextMapEntryForIterator:
155
case InlinableNative::IntrinsicGuardToSetObject:
156
case InlinableNative::IntrinsicGetNextSetEntryForIterator:
157
case InlinableNative::IntrinsicGuardToArrayBuffer:
158
case InlinableNative::IntrinsicArrayBufferByteLength:
159
case InlinableNative::IntrinsicPossiblyWrappedArrayBufferByteLength:
160
case InlinableNative::IntrinsicGuardToSharedArrayBuffer:
161
case InlinableNative::IntrinsicIsTypedArrayConstructor:
162
case InlinableNative::IntrinsicIsTypedArray:
163
case InlinableNative::IntrinsicIsPossiblyWrappedTypedArray:
164
case InlinableNative::IntrinsicPossiblyWrappedTypedArrayLength:
165
case InlinableNative::IntrinsicTypedArrayLength:
166
case InlinableNative::IntrinsicTypedArrayByteOffset:
167
case InlinableNative::IntrinsicTypedArrayElementShift:
168
case InlinableNative::IntrinsicArrayIteratorPrototypeOptimizable:
169
MOZ_CRASH("Unexpected cross-realm intrinsic call");
170
171
case InlinableNative::TestBailout:
172
case InlinableNative::TestAssertFloat32:
173
case InlinableNative::TestAssertRecoveredOnBailout:
174
// Testing functions, not worth inlining cross-realm.
175
return false;
176
177
case InlinableNative::ArrayIsArray:
178
case InlinableNative::ArrayJoin:
179
case InlinableNative::ArrayPop:
180
case InlinableNative::ArrayShift:
181
case InlinableNative::ArrayPush:
182
case InlinableNative::ArraySlice:
183
case InlinableNative::AtomicsCompareExchange:
184
case InlinableNative::AtomicsExchange:
185
case InlinableNative::AtomicsLoad:
186
case InlinableNative::AtomicsStore:
187
case InlinableNative::AtomicsAdd:
188
case InlinableNative::AtomicsSub:
189
case InlinableNative::AtomicsAnd:
190
case InlinableNative::AtomicsOr:
191
case InlinableNative::AtomicsXor:
192
case InlinableNative::AtomicsIsLockFree:
193
case InlinableNative::ReflectGetPrototypeOf:
194
case InlinableNative::String:
195
case InlinableNative::StringCharCodeAt:
196
case InlinableNative::StringFromCharCode:
197
case InlinableNative::StringFromCodePoint:
198
case InlinableNative::StringCharAt:
199
case InlinableNative::StringToLowerCase:
200
case InlinableNative::StringToUpperCase:
201
case InlinableNative::Object:
202
case InlinableNative::ObjectCreate:
203
case InlinableNative::ObjectIs:
204
case InlinableNative::ObjectToString:
205
case InlinableNative::TypedArrayConstructor:
206
// Default to false for most natives.
207
return false;
208
209
case InlinableNative::Limit:
210
break;
211
}
212
MOZ_CRASH("Unknown native");
213
}
214
215
IonBuilder::InliningResult IonBuilder::inlineNativeCall(CallInfo& callInfo,
216
JSFunction* target) {
217
MOZ_ASSERT(target->isNative());
218
219
if (!optimizationInfo().inlineNative()) {
220
return InliningStatus_NotInlined;
221
}
222
223
bool isWasmCall = target->isWasmWithJitEntry();
224
if (!isWasmCall &&
225
(!target->hasJitInfo() ||
226
target->jitInfo()->type() != JSJitInfo::InlinableNative)) {
227
// Reaching here means we tried to inline a native for which there is no
228
// Ion specialization.
229
return InliningStatus_NotInlined;
230
}
231
232
// Don't inline if we're constructing and new.target != callee. This can
233
// happen with Reflect.construct or derived class constructors.
234
if (callInfo.constructing() && callInfo.getNewTarget() != callInfo.fun()) {
235
return InliningStatus_NotInlined;
236
}
237
238
if (shouldAbortOnPreliminaryGroups(callInfo.thisArg())) {
239
return InliningStatus_NotInlined;
240
}
241
for (size_t i = 0; i < callInfo.argc(); i++) {
242
if (shouldAbortOnPreliminaryGroups(callInfo.getArg(i))) {
243
return InliningStatus_NotInlined;
244
}
245
}
246
247
if (isWasmCall) {
248
return inlineWasmCall(callInfo, target);
249
}
250
251
InlinableNative inlNative = target->jitInfo()->inlinableNative;
252
253
if (target->realm() != script()->realm() && !CanInlineCrossRealm(inlNative)) {
254
return InliningStatus_NotInlined;
255
}
256
257
switch (inlNative) {
258
// Array natives.
259
case InlinableNative::Array:
260
return inlineArray(callInfo, target->realm());
261
case InlinableNative::ArrayIsArray:
262
return inlineArrayIsArray(callInfo);
263
case InlinableNative::ArrayJoin:
264
return inlineArrayJoin(callInfo);
265
case InlinableNative::ArrayPop:
266
return inlineArrayPopShift(callInfo, MArrayPopShift::Pop);
267
case InlinableNative::ArrayShift:
268
return inlineArrayPopShift(callInfo, MArrayPopShift::Shift);
269
case InlinableNative::ArrayPush:
270
return inlineArrayPush(callInfo);
271
case InlinableNative::ArraySlice:
272
return inlineArraySlice(callInfo);
273
274
// Array intrinsics.
275
case InlinableNative::IntrinsicNewArrayIterator:
276
return inlineNewIterator(callInfo, MNewIterator::ArrayIterator);
277
case InlinableNative::IntrinsicArrayIteratorPrototypeOptimizable:
278
return inlineArrayIteratorPrototypeOptimizable(callInfo);
279
280
// Atomic natives.
281
case InlinableNative::AtomicsCompareExchange:
282
return inlineAtomicsCompareExchange(callInfo);
283
case InlinableNative::AtomicsExchange:
284
return inlineAtomicsExchange(callInfo);
285
case InlinableNative::AtomicsLoad:
286
return inlineAtomicsLoad(callInfo);
287
case InlinableNative::AtomicsStore:
288
return inlineAtomicsStore(callInfo);
289
case InlinableNative::AtomicsAdd:
290
case InlinableNative::AtomicsSub:
291
case InlinableNative::AtomicsAnd:
292
case InlinableNative::AtomicsOr:
293
case InlinableNative::AtomicsXor:
294
return inlineAtomicsBinop(callInfo, inlNative);
295
case InlinableNative::AtomicsIsLockFree:
296
return inlineAtomicsIsLockFree(callInfo);
297
298
// Boolean natives.
299
case InlinableNative::Boolean:
300
return inlineBoolean(callInfo);
301
302
#ifdef JS_HAS_INTL_API
303
// Intl natives.
304
case InlinableNative::IntlGuardToCollator:
305
return inlineGuardToClass(callInfo, &CollatorObject::class_);
306
case InlinableNative::IntlGuardToDateTimeFormat:
307
return inlineGuardToClass(callInfo, &DateTimeFormatObject::class_);
308
case InlinableNative::IntlGuardToListFormat:
309
return inlineGuardToClass(callInfo, &ListFormatObject::class_);
310
case InlinableNative::IntlGuardToNumberFormat:
311
return inlineGuardToClass(callInfo, &NumberFormatObject::class_);
312
case InlinableNative::IntlGuardToPluralRules:
313
return inlineGuardToClass(callInfo, &PluralRulesObject::class_);
314
case InlinableNative::IntlGuardToRelativeTimeFormat:
315
return inlineGuardToClass(callInfo, &RelativeTimeFormatObject::class_);
316
#else
317
case InlinableNative::IntlGuardToCollator:
318
case InlinableNative::IntlGuardToDateTimeFormat:
319
case InlinableNative::IntlGuardToListFormat:
320
case InlinableNative::IntlGuardToNumberFormat:
321
case InlinableNative::IntlGuardToPluralRules:
322
case InlinableNative::IntlGuardToRelativeTimeFormat:
323
MOZ_CRASH("Intl API disabled");
324
#endif
325
326
// Math natives.
327
case InlinableNative::MathAbs:
328
return inlineMathAbs(callInfo);
329
case InlinableNative::MathFloor:
330
return inlineMathFloor(callInfo);
331
case InlinableNative::MathCeil:
332
return inlineMathCeil(callInfo);
333
case InlinableNative::MathRound:
334
return inlineMathRound(callInfo);
335
case InlinableNative::MathClz32:
336
return inlineMathClz32(callInfo);
337
case InlinableNative::MathSqrt:
338
return inlineMathSqrt(callInfo);
339
case InlinableNative::MathATan2:
340
return inlineMathAtan2(callInfo);
341
case InlinableNative::MathHypot:
342
return inlineMathHypot(callInfo);
343
case InlinableNative::MathMax:
344
return inlineMathMinMax(callInfo, true /* max */);
345
case InlinableNative::MathMin:
346
return inlineMathMinMax(callInfo, false /* max */);
347
case InlinableNative::MathPow:
348
return inlineMathPow(callInfo);
349
case InlinableNative::MathRandom:
350
return inlineMathRandom(callInfo);
351
case InlinableNative::MathImul:
352
return inlineMathImul(callInfo);
353
case InlinableNative::MathFRound:
354
return inlineMathFRound(callInfo);
355
case InlinableNative::MathTrunc:
356
return inlineMathTrunc(callInfo);
357
case InlinableNative::MathSign:
358
return inlineMathSign(callInfo);
359
case InlinableNative::MathSin:
360
return inlineMathFunction(callInfo, MMathFunction::Sin);
361
case InlinableNative::MathTan:
362
return inlineMathFunction(callInfo, MMathFunction::Tan);
363
case InlinableNative::MathCos:
364
return inlineMathFunction(callInfo, MMathFunction::Cos);
365
case InlinableNative::MathExp:
366
return inlineMathFunction(callInfo, MMathFunction::Exp);
367
case InlinableNative::MathLog:
368
return inlineMathFunction(callInfo, MMathFunction::Log);
369
case InlinableNative::MathASin:
370
return inlineMathFunction(callInfo, MMathFunction::ASin);
371
case InlinableNative::MathATan:
372
return inlineMathFunction(callInfo, MMathFunction::ATan);
373
case InlinableNative::MathACos:
374
return inlineMathFunction(callInfo, MMathFunction::ACos);
375
case InlinableNative::MathLog10:
376
return inlineMathFunction(callInfo, MMathFunction::Log10);
377
case InlinableNative::MathLog2:
378
return inlineMathFunction(callInfo, MMathFunction::Log2);
379
case InlinableNative::MathLog1P:
380
return inlineMathFunction(callInfo, MMathFunction::Log1P);
381
case InlinableNative::MathExpM1:
382
return inlineMathFunction(callInfo, MMathFunction::ExpM1);
383
case InlinableNative::MathCosH:
384
return inlineMathFunction(callInfo, MMathFunction::CosH);
385
case InlinableNative::MathSinH:
386
return inlineMathFunction(callInfo, MMathFunction::SinH);
387
case InlinableNative::MathTanH:
388
return inlineMathFunction(callInfo, MMathFunction::TanH);
389
case InlinableNative::MathACosH:
390
return inlineMathFunction(callInfo, MMathFunction::ACosH);
391
case InlinableNative::MathASinH:
392
return inlineMathFunction(callInfo, MMathFunction::ASinH);
393
case InlinableNative::MathATanH:
394
return inlineMathFunction(callInfo, MMathFunction::ATanH);
395
case InlinableNative::MathCbrt:
396
return inlineMathFunction(callInfo, MMathFunction::Cbrt);
397
398
// Reflect natives.
399
case InlinableNative::ReflectGetPrototypeOf:
400
return inlineReflectGetPrototypeOf(callInfo);
401
402
// RegExp natives.
403
case InlinableNative::RegExpMatcher:
404
return inlineRegExpMatcher(callInfo);
405
case InlinableNative::RegExpSearcher:
406
return inlineRegExpSearcher(callInfo);
407
case InlinableNative::RegExpTester:
408
return inlineRegExpTester(callInfo);
409
case InlinableNative::IsRegExpObject:
410
return inlineIsRegExpObject(callInfo);
411
case InlinableNative::IsPossiblyWrappedRegExpObject:
412
return inlineIsPossiblyWrappedRegExpObject(callInfo);
413
case InlinableNative::RegExpPrototypeOptimizable:
414
return inlineRegExpPrototypeOptimizable(callInfo);
415
case InlinableNative::RegExpInstanceOptimizable:
416
return inlineRegExpInstanceOptimizable(callInfo);
417
case InlinableNative::GetFirstDollarIndex:
418
return inlineGetFirstDollarIndex(callInfo);
419
case InlinableNative::IntrinsicNewRegExpStringIterator:
420
return inlineNewIterator(callInfo, MNewIterator::RegExpStringIterator);
421
422
// String natives.
423
case InlinableNative::String:
424
return inlineStringObject(callInfo);
425
case InlinableNative::StringCharCodeAt:
426
return inlineStrCharCodeAt(callInfo);
427
case InlinableNative::StringFromCharCode:
428
return inlineStrFromCharCode(callInfo);
429
case InlinableNative::StringFromCodePoint:
430
return inlineStrFromCodePoint(callInfo);
431
case InlinableNative::StringCharAt:
432
return inlineStrCharAt(callInfo);
433
case InlinableNative::StringToLowerCase:
434
return inlineStringConvertCase(callInfo, MStringConvertCase::LowerCase);
435
case InlinableNative::StringToUpperCase:
436
return inlineStringConvertCase(callInfo, MStringConvertCase::UpperCase);
437
438
// String intrinsics.
439
case InlinableNative::IntrinsicStringReplaceString:
440
return inlineStringReplaceString(callInfo);
441
case InlinableNative::IntrinsicStringSplitString:
442
return inlineStringSplitString(callInfo);
443
case InlinableNative::IntrinsicNewStringIterator:
444
return inlineNewIterator(callInfo, MNewIterator::StringIterator);
445
446
// Object natives.
447
case InlinableNative::Object:
448
return inlineObject(callInfo);
449
case InlinableNative::ObjectCreate:
450
return inlineObjectCreate(callInfo);
451
case InlinableNative::ObjectIs:
452
return inlineObjectIs(callInfo);
453
case InlinableNative::ObjectToString:
454
return inlineObjectToString(callInfo);
455
456
// Testing functions.
457
case InlinableNative::TestBailout:
458
return inlineBailout(callInfo);
459
case InlinableNative::TestAssertFloat32:
460
return inlineAssertFloat32(callInfo);
461
case InlinableNative::TestAssertRecoveredOnBailout:
462
return inlineAssertRecoveredOnBailout(callInfo);
463
464
// Slot intrinsics.
465
case InlinableNative::IntrinsicUnsafeSetReservedSlot:
466
return inlineUnsafeSetReservedSlot(callInfo);
467
case InlinableNative::IntrinsicUnsafeGetReservedSlot:
468
return inlineUnsafeGetReservedSlot(callInfo, MIRType::Value);
469
case InlinableNative::IntrinsicUnsafeGetObjectFromReservedSlot:
470
return inlineUnsafeGetReservedSlot(callInfo, MIRType::Object);
471
case InlinableNative::IntrinsicUnsafeGetInt32FromReservedSlot:
472
return inlineUnsafeGetReservedSlot(callInfo, MIRType::Int32);
473
case InlinableNative::IntrinsicUnsafeGetStringFromReservedSlot:
474
return inlineUnsafeGetReservedSlot(callInfo, MIRType::String);
475
case InlinableNative::IntrinsicUnsafeGetBooleanFromReservedSlot:
476
return inlineUnsafeGetReservedSlot(callInfo, MIRType::Boolean);
477
478
// Utility intrinsics.
479
case InlinableNative::IntrinsicIsCallable:
480
return inlineIsCallable(callInfo);
481
case InlinableNative::IntrinsicIsConstructor:
482
return inlineIsConstructor(callInfo);
483
case InlinableNative::IntrinsicToObject:
484
return inlineToObject(callInfo);
485
case InlinableNative::IntrinsicIsObject:
486
return inlineIsObject(callInfo);
487
case InlinableNative::IntrinsicIsCrossRealmArrayConstructor:
488
return inlineIsCrossRealmArrayConstructor(callInfo);
489
case InlinableNative::IntrinsicToInteger:
490
return inlineToInteger(callInfo);
491
case InlinableNative::IntrinsicToString:
492
return inlineToString(callInfo);
493
case InlinableNative::IntrinsicIsConstructing:
494
return inlineIsConstructing(callInfo);
495
case InlinableNative::IntrinsicSubstringKernel:
496
return inlineSubstringKernel(callInfo);
497
case InlinableNative::IntrinsicGuardToArrayIterator:
498
return inlineGuardToClass(callInfo, &ArrayIteratorObject::class_);
499
case InlinableNative::IntrinsicGuardToMapIterator:
500
return inlineGuardToClass(callInfo, &MapIteratorObject::class_);
501
case InlinableNative::IntrinsicGuardToSetIterator:
502
return inlineGuardToClass(callInfo, &SetIteratorObject::class_);
503
case InlinableNative::IntrinsicGuardToStringIterator:
504
return inlineGuardToClass(callInfo, &StringIteratorObject::class_);
505
case InlinableNative::IntrinsicGuardToRegExpStringIterator:
506
return inlineGuardToClass(callInfo, &RegExpStringIteratorObject::class_);
507
case InlinableNative::IntrinsicObjectHasPrototype:
508
return inlineObjectHasPrototype(callInfo);
509
case InlinableNative::IntrinsicFinishBoundFunctionInit:
510
return inlineFinishBoundFunctionInit(callInfo);
511
case InlinableNative::IntrinsicIsPackedArray:
512
return inlineIsPackedArray(callInfo);
513
514
// Map intrinsics.
515
case InlinableNative::IntrinsicGuardToMapObject:
516
return inlineGuardToClass(callInfo, &MapObject::class_);
517
case InlinableNative::IntrinsicGetNextMapEntryForIterator:
518
return inlineGetNextEntryForIterator(callInfo,
519
MGetNextEntryForIterator::Map);
520
521
// Set intrinsics.
522
case InlinableNative::IntrinsicGuardToSetObject:
523
return inlineGuardToClass(callInfo, &SetObject::class_);
524
case InlinableNative::IntrinsicGetNextSetEntryForIterator:
525
return inlineGetNextEntryForIterator(callInfo,
526
MGetNextEntryForIterator::Set);
527
528
// ArrayBuffer intrinsics.
529
case InlinableNative::IntrinsicGuardToArrayBuffer:
530
return inlineGuardToClass(callInfo, &ArrayBufferObject::class_);
531
case InlinableNative::IntrinsicArrayBufferByteLength:
532
return inlineArrayBufferByteLength(callInfo);
533
case InlinableNative::IntrinsicPossiblyWrappedArrayBufferByteLength:
534
return inlinePossiblyWrappedArrayBufferByteLength(callInfo);
535
536
// SharedArrayBuffer intrinsics.
537
case InlinableNative::IntrinsicGuardToSharedArrayBuffer:
538
return inlineGuardToClass(callInfo, &SharedArrayBufferObject::class_);
539
540
// TypedArray intrinsics.
541
case InlinableNative::TypedArrayConstructor:
542
return inlineTypedArray(callInfo, target->native());
543
case InlinableNative::IntrinsicIsTypedArrayConstructor:
544
return inlineIsTypedArrayConstructor(callInfo);
545
case InlinableNative::IntrinsicIsTypedArray:
546
return inlineIsTypedArray(callInfo);
547
case InlinableNative::IntrinsicIsPossiblyWrappedTypedArray:
548
return inlineIsPossiblyWrappedTypedArray(callInfo);
549
case InlinableNative::IntrinsicPossiblyWrappedTypedArrayLength:
550
return inlinePossiblyWrappedTypedArrayLength(callInfo);
551
case InlinableNative::IntrinsicTypedArrayLength:
552
return inlineTypedArrayLength(callInfo);
553
case InlinableNative::IntrinsicTypedArrayByteOffset:
554
return inlineTypedArrayByteOffset(callInfo);
555
case InlinableNative::IntrinsicTypedArrayElementShift:
556
return inlineTypedArrayElementShift(callInfo);
557
558
case InlinableNative::Limit:
559
break;
560
}
561
562
MOZ_CRASH("Shouldn't get here");
563
}
564
565
IonBuilder::InliningResult IonBuilder::inlineNativeGetter(CallInfo& callInfo,
566
JSFunction* target) {
567
MOZ_ASSERT(target->isNative());
568
JSNative native = target->native();
569
570
if (!optimizationInfo().inlineNative()) {
571
return InliningStatus_NotInlined;
572
}
573
574
MDefinition* thisArg = callInfo.thisArg();
575
TemporaryTypeSet* thisTypes = thisArg->resultTypeSet();
576
MOZ_ASSERT(callInfo.argc() == 0);
577
578
if (!thisTypes) {
579
return InliningStatus_NotInlined;
580
}
581
582
// Note: target might be a cross-realm native!
583
584
// Try to optimize typed array lengths.
585
if (TypedArrayObject::isOriginalLengthGetter(native)) {
586
if (thisTypes->forAllClasses(constraints(), IsTypedArrayClass) !=
587
TemporaryTypeSet::ForAllResult::ALL_TRUE) {
588
return InliningStatus_NotInlined;
589
}
590
591
MInstruction* length = addTypedArrayLength(thisArg);
592
current->push(length);
593
return InliningStatus_Inlined;
594
}
595
596
// Try to optimize typed array byteOffsets.
597
if (TypedArrayObject::isOriginalByteOffsetGetter(native)) {
598
if (thisTypes->forAllClasses(constraints(), IsTypedArrayClass) !=
599
TemporaryTypeSet::ForAllResult::ALL_TRUE) {
600
return InliningStatus_NotInlined;
601
}
602
603
MInstruction* byteOffset = addTypedArrayByteOffset(thisArg);
604
current->push(byteOffset);
605
return InliningStatus_Inlined;
606
}
607
608
// Try to optimize RegExp getters.
609
RegExpFlags mask = RegExpFlag::NoFlags;
610
if (RegExpObject::isOriginalFlagGetter(native, &mask)) {
611
const JSClass* clasp = thisTypes->getKnownClass(constraints());
612
if (clasp != &RegExpObject::class_) {
613
return InliningStatus_NotInlined;
614
}
615
616
MLoadFixedSlot* flags =
617
MLoadFixedSlot::New(alloc(), thisArg, RegExpObject::flagsSlot());
618
current->add(flags);
619
flags->setResultType(MIRType::Int32);
620
MConstant* maskConst = MConstant::New(alloc(), Int32Value(mask.value()));
621
current->add(maskConst);
622
MBitAnd* maskedFlag = MBitAnd::New(alloc(), flags, maskConst);
623
maskedFlag->setInt32Specialization();
624
current->add(maskedFlag);
625
626
MDefinition* result = convertToBoolean(maskedFlag);
627
current->push(result);
628
return InliningStatus_Inlined;
629
}
630
631
return InliningStatus_NotInlined;
632
}
633
634
TemporaryTypeSet* IonBuilder::getInlineReturnTypeSet() {
635
return bytecodeTypes(pc);
636
}
637
638
MIRType IonBuilder::getInlineReturnType() {
639
TemporaryTypeSet* returnTypes = getInlineReturnTypeSet();
640
return returnTypes->getKnownMIRType();
641
}
642
643
IonBuilder::InliningResult IonBuilder::inlineMathFunction(
644
CallInfo& callInfo, MMathFunction::Function function) {
645
if (callInfo.constructing()) {
646
return InliningStatus_NotInlined;
647
}
648
649
if (callInfo.argc() != 1) {
650
return InliningStatus_NotInlined;
651
}
652
653
if (getInlineReturnType() != MIRType::Double) {
654
return InliningStatus_NotInlined;
655
}
656
if (!IsNumberType(callInfo.getArg(0)->type())) {
657
return InliningStatus_NotInlined;
658
}
659
660
callInfo.fun()->setImplicitlyUsedUnchecked();
661
callInfo.thisArg()->setImplicitlyUsedUnchecked();
662
663
MMathFunction* ins =
664
MMathFunction::New(alloc(), callInfo.getArg(0), function);
665
current->add(ins);
666
current->push(ins);
667
return InliningStatus_Inlined;
668
}
669
670
IonBuilder::InliningResult IonBuilder::inlineArray(CallInfo& callInfo,
671
Realm* targetRealm) {
672
uint32_t initLength = 0;
673
674
JSObject* templateObject =
675
inspector->getTemplateObjectForNative(pc, ArrayConstructor);
676
// This is shared by ArrayConstructor and array_construct (std_Array).
677
if (!templateObject) {
678
templateObject = inspector->getTemplateObjectForNative(pc, array_construct);
679
}
680
681
if (!templateObject) {
682
return InliningStatus_NotInlined;
683
}
684
685
if (templateObject->nonCCWRealm() != targetRealm) {
686
return InliningStatus_NotInlined;
687
}
688
689
// Multiple arguments imply array initialization, not just construction.
690
if (callInfo.argc() >= 2) {
691
initLength = callInfo.argc();
692
693
TypeSet::ObjectKey* key = TypeSet::ObjectKey::get(templateObject);
694
if (!key->unknownProperties()) {
695
HeapTypeSetKey elemTypes = key->property(JSID_VOID);
696
697
for (uint32_t i = 0; i < initLength; i++) {
698
MDefinition* value = callInfo.getArg(i);
699
if (!TypeSetIncludes(elemTypes.maybeTypes(), value->type(),
700
value->resultTypeSet())) {
701
elemTypes.freeze(constraints());
702
return InliningStatus_NotInlined;
703
}
704
}
705
}
706
}
707
708
// A single integer argument denotes initial length.
709
if (callInfo.argc() == 1) {
710
MDefinition* arg = callInfo.getArg(0);
711
if (arg->type() != MIRType::Int32) {
712
return InliningStatus_NotInlined;
713
}
714
715
if (!arg->isConstant()) {
716
callInfo.setImplicitlyUsedUnchecked();
717
MNewArrayDynamicLength* ins = MNewArrayDynamicLength::New(
718
alloc(), constraints(), templateObject,
719
templateObject->group()->initialHeap(constraints()), arg);
720
current->add(ins);
721
current->push(ins);
722
723
// This may throw, so we need a resume point.
724
MOZ_TRY(resumeAfter(ins));
725
726
return InliningStatus_Inlined;
727
}
728
729
// Negative lengths generate a RangeError, unhandled by the inline path.
730
initLength = arg->toConstant()->toInt32();
731
if (initLength > NativeObject::MAX_DENSE_ELEMENTS_COUNT) {
732
return InliningStatus_NotInlined;
733
}
734
MOZ_ASSERT(initLength <= INT32_MAX);
735
736
// Make sure initLength matches the template object's length. This is
737
// not guaranteed to be the case, for instance if we're inlining the
738
// MConstant may come from an outer script.
739
if (initLength != templateObject->as<ArrayObject>().length()) {
740
return InliningStatus_NotInlined;
741
}
742
743
// Don't inline large allocations.
744
if (initLength > ArrayObject::EagerAllocationMaxLength) {
745
return InliningStatus_NotInlined;
746
}
747
}
748
749
callInfo.setImplicitlyUsedUnchecked();
750
751
MOZ_TRY(jsop_newarray(templateObject, initLength));
752
753
MNewArray* array = current->peek(-1)->toNewArray();
754
if (callInfo.argc() >= 2) {
755
for (uint32_t i = 0; i < initLength; i++) {
756
if (!alloc().ensureBallast()) {
757
return abort(AbortReason::Alloc);
758
}
759
MDefinition* value = callInfo.getArg(i);
760
761
MConstant* id = MConstant::New(alloc(), Int32Value(i));
762
current->add(id);
763
764
MOZ_TRY(initArrayElementFastPath(array, id, value,
765
/* addResumePoint = */ false));
766
}
767
768
MInstruction* setLength = setInitializedLength(array, initLength);
769
MOZ_TRY(resumeAfter(setLength));
770
}
771
772
return InliningStatus_Inlined;
773
}
774
775
static bool IsArrayClass(const JSClass* clasp) {
776
return clasp == &ArrayObject::class_;
777
}
778
779
IonBuilder::InliningResult IonBuilder::inlineArrayIsArray(CallInfo& callInfo) {
780
if (callInfo.constructing() || callInfo.argc() != 1) {
781
return InliningStatus_NotInlined;
782
}
783
784
if (getInlineReturnType() != MIRType::Boolean) {
785
return InliningStatus_NotInlined;
786
}
787
788
MDefinition* arg = callInfo.getArg(0);
789
790
if (!arg->mightBeType(MIRType::Object)) {
791
pushConstant(BooleanValue(false));
792
callInfo.setImplicitlyUsedUnchecked();
793
return InliningStatus_Inlined;
794
}
795
796
using ForAllResult = TemporaryTypeSet::ForAllResult;
797
798
TemporaryTypeSet* types = arg->resultTypeSet();
799
800
// Fast path for non-proxy objects.
801
if (arg->type() == MIRType::Object && types &&
802
types->forAllClasses(constraints(), IsProxyClass) ==
803
ForAllResult::ALL_FALSE) {
804
// Definitely not a proxy. Now check for the array classes.
805
ForAllResult result = types->forAllClasses(constraints(), IsArrayClass);
806
807
if (result == ForAllResult::ALL_FALSE || result == ForAllResult::ALL_TRUE) {
808
// Definitely an array or definitely not an array, so we can
809
// constant fold.
810
pushConstant(BooleanValue(result == ForAllResult::ALL_TRUE));
811
callInfo.setImplicitlyUsedUnchecked();
812
return InliningStatus_Inlined;
813
}
814
815
// We have some array classes and some non-array classes, so we have to
816
// check at runtime.
817
MOZ_ASSERT(result == ForAllResult::MIXED);
818
819
MHasClass* hasClass = MHasClass::New(alloc(), arg, &ArrayObject::class_);
820
current->add(hasClass);
821
current->push(hasClass);
822
823
callInfo.setImplicitlyUsedUnchecked();
824
return InliningStatus_Inlined;
825
}
826
827
// The value might be a primitive or a proxy. MIsArray handles these cases.
828
MIsArray* isArray = MIsArray::New(alloc(), arg);
829
current->add(isArray);
830
current->push(isArray);
831
832
MOZ_TRY(resumeAfter(isArray));
833
834
callInfo.setImplicitlyUsedUnchecked();
835
return InliningStatus_Inlined;
836
}
837
838
IonBuilder::InliningResult IonBuilder::inlineArrayPopShift(
839
CallInfo& callInfo, MArrayPopShift::Mode mode) {
840
if (callInfo.constructing()) {
841
return InliningStatus_NotInlined;
842
}
843
844
MIRType returnType = getInlineReturnType();
845
if (returnType == MIRType::Undefined || returnType == MIRType::Null) {
846
return InliningStatus_NotInlined;
847
}
848
if (callInfo.thisArg()->type() != MIRType::Object) {
849
return InliningStatus_NotInlined;
850
}
851
852
// Pop and shift are only handled for dense arrays that have never been
853
// used in an iterator: popping elements does not account for suppressing
854
// deleted properties in active iterators.
855
// Don't optimize shift if the array may be non-extensible (this matters
856
// when there are holes). Don't optimize pop if the array may be
857
// non-extensible, so we don't need to adjust the capacity for
858
// non-extensible arrays (non-extensible objects always have a capacity
859
// equal to their initialized length). We check this here because there's
860
// no non-extensible ObjectElements flag so we would need an extra guard
861
// on the BaseShape flags.
862
ObjectGroupFlags unhandledFlags =
863
OBJECT_FLAG_SPARSE_INDEXES | OBJECT_FLAG_LENGTH_OVERFLOW |
864
OBJECT_FLAG_ITERATED | OBJECT_FLAG_NON_EXTENSIBLE_ELEMENTS;
865
866
MDefinition* obj = callInfo.thisArg();
867
TemporaryTypeSet* thisTypes = obj->resultTypeSet();
868
if (!thisTypes) {
869
return InliningStatus_NotInlined;
870
}
871
const JSClass* clasp = thisTypes->getKnownClass(constraints());
872
if (clasp != &ArrayObject::class_) {
873
return InliningStatus_NotInlined;
874
}
875
if (thisTypes->hasObjectFlags(constraints(), unhandledFlags)) {
876
return InliningStatus_NotInlined;
877
}
878
879
// Watch out for extra indexed properties on the object or its prototype.
880
bool hasIndexedProperty;
881
MOZ_TRY_VAR(hasIndexedProperty,
882
ElementAccessHasExtraIndexedProperty(this, obj));
883
if (hasIndexedProperty) {
884
return InliningStatus_NotInlined;
885
}
886
887
callInfo.setImplicitlyUsedUnchecked();
888
889
obj = addMaybeCopyElementsForWrite(obj, /* checkNative = */ false);
890
891
TemporaryTypeSet* returnTypes = getInlineReturnTypeSet();
892
bool needsHoleCheck =
893
thisTypes->hasObjectFlags(constraints(), OBJECT_FLAG_NON_PACKED);
894
bool maybeUndefined = returnTypes->hasType(TypeSet::UndefinedType());
895
896
BarrierKind barrier = PropertyReadNeedsTypeBarrier(
897
analysisContext, alloc(), constraints(), obj, nullptr, returnTypes);
898
if (barrier != BarrierKind::NoBarrier) {
899
returnType = MIRType::Value;
900
}
901
902
MArrayPopShift* ins =
903
MArrayPopShift::New(alloc(), obj, mode, needsHoleCheck, maybeUndefined);
904
current->add(ins);
905
current->push(ins);
906
ins->setResultType(returnType);
907
908
MOZ_TRY(resumeAfter(ins));
909
MOZ_TRY(pushTypeBarrier(ins, returnTypes, barrier));
910
return InliningStatus_Inlined;
911
}
912
913
IonBuilder::InliningResult IonBuilder::inlineArrayJoin(CallInfo& callInfo) {
914
if (callInfo.argc() != 1 || callInfo.constructing()) {
915
return InliningStatus_NotInlined;
916
}
917
918
if (getInlineReturnType() != MIRType::String) {
919
return InliningStatus_NotInlined;
920
}
921
if (callInfo.thisArg()->type() != MIRType::Object) {
922
return InliningStatus_NotInlined;
923
}
924
if (callInfo.getArg(0)->type() != MIRType::String) {
925
return InliningStatus_NotInlined;
926
}
927
928
// If we can confirm that the class is an array, the codegen
929
// for MArrayJoin can be notified to check for common empty and one-item
930
// arrays.
931
bool optimizeForArray = ([&]() {
932
TemporaryTypeSet* thisTypes = callInfo.thisArg()->resultTypeSet();
933
if (!thisTypes) {
934
return false;
935
}
936
937
const JSClass* clasp = thisTypes->getKnownClass(constraints());
938
if (clasp != &ArrayObject::class_) {
939
return false;
940
}
941
942
return true;
943
})();
944
945
callInfo.setImplicitlyUsedUnchecked();
946
947
MArrayJoin* ins = MArrayJoin::New(alloc(), callInfo.thisArg(),
948
callInfo.getArg(0), optimizeForArray);
949
950
current->add(ins);
951
current->push(ins);
952
953
MOZ_TRY(resumeAfter(ins));
954
return InliningStatus_Inlined;
955
}
956
957
IonBuilder::InliningResult IonBuilder::inlineArrayPush(CallInfo& callInfo) {
958
const uint32_t inlineArgsLimit = 10;
959
if (callInfo.argc() < 1 || callInfo.argc() > inlineArgsLimit ||
960
callInfo.constructing()) {
961
return InliningStatus_NotInlined;
962
}
963
964
// XXX bug 1493903.
965
if (callInfo.argc() != 1) {
966
return InliningStatus_NotInlined;
967
}
968
969
MDefinition* obj = callInfo.thisArg();
970
for (uint32_t i = 0; i < callInfo.argc(); i++) {
971
MDefinition* value = callInfo.getArg(i);
972
if (PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current, &obj,
973
nullptr, &value,
974
/* canModify = */ false)) {
975
return InliningStatus_NotInlined;
976
}
977
}
978
979
if (getInlineReturnType() != MIRType::Int32) {
980
return InliningStatus_NotInlined;
981
}
982
if (obj->type() != MIRType::Object) {
983
return InliningStatus_NotInlined;
984
}
985
986
TemporaryTypeSet* thisTypes = obj->resultTypeSet();
987
if (!thisTypes) {
988
return InliningStatus_NotInlined;
989
}
990
const JSClass* clasp = thisTypes->getKnownClass(constraints());
991
if (clasp != &ArrayObject::class_) {
992
return InliningStatus_NotInlined;
993
}
994
995
bool hasIndexedProperty;
996
MOZ_TRY_VAR(hasIndexedProperty,
997
ElementAccessHasExtraIndexedProperty(this, obj));
998
if (hasIndexedProperty) {
999
return InliningStatus_NotInlined;
1000
}
1001
1002
TemporaryTypeSet::DoubleConversion conversion =
1003
thisTypes->convertDoubleElements(constraints());
1004
if (conversion == TemporaryTypeSet::AmbiguousDoubleConversion) {
1005
return InliningStatus_NotInlined;
1006
}
1007
1008
callInfo.setImplicitlyUsedUnchecked();
1009
1010
bool toDouble = conversion == TemporaryTypeSet::AlwaysConvertToDoubles ||
1011
conversion == TemporaryTypeSet::MaybeConvertToDoubles;
1012
1013
obj = addMaybeCopyElementsForWrite(obj, /* checkNative = */ false);
1014
1015
// If we have more than one argument, we are splitting the function into
1016
// multiple inlined calls to Array.push.
1017
//
1018
// Still, in case of bailouts, we have to keep the atomicity of the call, as
1019
// we cannot resume within Array.push function. To do so, we register a
1020
// resume point which would be captured by the upcoming MArrayPush
1021
// instructions, and this resume point contains an instruction which
1022
// truncates the length of the Array, to its original length in case of
1023
// bailouts, and resume before the Array.push call.
1024
MResumePoint* lastRp = nullptr;
1025
MInstruction* truncate = nullptr;
1026
if (callInfo.argc() > 1) {
1027
MInstruction* elements = MElements::New(alloc(), obj);
1028
MInstruction* length = MArrayLength::New(alloc(), elements);
1029
truncate = MSetArrayLength::New(alloc(), obj, length);
1030
truncate->setRecoveredOnBailout();
1031
1032
current->add(elements);
1033
current->add(length);
1034
current->add(truncate);
1035
1036
// Restore the stack, such that resume points are created with the stack
1037
// as it was before the call.
1038
MOZ_TRY(callInfo.pushPriorCallStack(&mirGen_, current));
1039
}
1040
1041
MInstruction* ins = nullptr;
1042
for (uint32_t i = 0; i < callInfo.argc(); i++) {
1043
MDefinition* value = callInfo.getArg(i);
1044
if (toDouble) {
1045
MInstruction* valueDouble = MToDouble::New(alloc(), value);
1046
current->add(valueDouble);
1047
value = valueDouble;
1048
}
1049
1050
if (needsPostBarrier(value)) {
1051
MInstruction* elements = MElements::New(alloc(), obj);
1052
current->add(elements);
1053
MInstruction* initLength = MInitializedLength::New(alloc(), elements);
1054
current->add(initLength);
1055
current->add(
1056
MPostWriteElementBarrier::New(alloc(), obj, value, initLength));
1057
}
1058
1059
ins = MArrayPush::New(alloc(), obj, value);
1060
current->add(ins);
1061
1062
if (callInfo.argc() > 1) {
1063
// Restore that call stack and the array length.
1064
MOZ_TRY(resumeAt(ins, pc));
1065
ins->resumePoint()->addStore(alloc(), truncate, lastRp);
1066
lastRp = ins->resumePoint();
1067
}
1068
}
1069
1070
if (callInfo.argc() > 1) {
1071
// Fix the stack to represent the state after the call execution.
1072
callInfo.popPriorCallStack(current);
1073
}
1074
current->push(ins);
1075
1076
if (callInfo.argc() > 1) {
1077
ins = MNop::New(alloc());
1078
current->add(ins);
1079
}
1080
1081
MOZ_TRY(resumeAfter(ins));
1082
return InliningStatus_Inlined;
1083
}
1084
1085
IonBuilder::InliningResult IonBuilder::inlineArraySlice(CallInfo& callInfo) {
1086
if (callInfo.constructing()) {
1087
return InliningStatus_NotInlined;
1088
}
1089
1090
MDefinition* obj = callInfo.thisArg();
1091
1092
// Ensure |this| and result are objects.
1093
if (getInlineReturnType() != MIRType::Object) {
1094
return InliningStatus_NotInlined;
1095
}
1096
if (obj->type() != MIRType::Object) {
1097
return InliningStatus_NotInlined;
1098
}
1099
1100
// Arguments for the sliced region must be integers.
1101
if (callInfo.argc() > 0) {
1102
if (callInfo.getArg(0)->type() != MIRType::Int32) {
1103
return InliningStatus_NotInlined;
1104
}
1105
if (callInfo.argc() > 1) {
1106
if (callInfo.getArg(1)->type() != MIRType::Int32) {
1107
return InliningStatus_NotInlined;
1108
}
1109
}
1110
}
1111
1112
// |this| must be a dense array.
1113
TemporaryTypeSet* thisTypes = obj->resultTypeSet();
1114
if (!thisTypes) {
1115
return InliningStatus_NotInlined;
1116
}
1117
1118
const JSClass* clasp = thisTypes->getKnownClass(constraints());
1119
if (clasp != &ArrayObject::class_) {
1120
return InliningStatus_NotInlined;
1121
}
1122
1123
// Watch out for extra indexed properties on the object or its prototype.
1124
bool hasIndexedProperty;
1125
MOZ_TRY_VAR(hasIndexedProperty,
1126
ElementAccessHasExtraIndexedProperty(this, obj));
1127
if (hasIndexedProperty) {
1128
return InliningStatus_NotInlined;
1129
}
1130
1131
// The group of the result will be dynamically fixed up to match the input
1132
// object, allowing us to handle 'this' objects that might have more than
1133
// one group. Make sure that no singletons can be sliced here.
1134
for (unsigned i = 0; i < thisTypes->getObjectCount(); i++) {
1135
TypeSet::ObjectKey* key = thisTypes->getObject(i);
1136
if (key && key->isSingleton()) {
1137
return InliningStatus_NotInlined;
1138
}
1139
}
1140
1141
// Inline the call.
1142
JSObject* templateObj =
1143
inspector->getTemplateObjectForNative(pc, js::array_slice);
1144
if (!templateObj) {
1145
return InliningStatus_NotInlined;
1146
}
1147
1148
if (!templateObj->is<ArrayObject>()) {
1149
return InliningStatus_NotInlined;
1150
}
1151
1152
callInfo.setImplicitlyUsedUnchecked();
1153
1154
MDefinition* begin;
1155
if (callInfo.argc() > 0) {
1156
begin = callInfo.getArg(0);
1157
} else {
1158
begin = constant(Int32Value(0));
1159
}
1160
1161
MDefinition* end;
1162
if (callInfo.argc() > 1) {
1163
end = callInfo.getArg(1);
1164
} else if (clasp == &ArrayObject::class_) {
1165
MElements* elements = MElements::New(alloc(), obj);
1166
current->add(elements);
1167
1168
end = MArrayLength::New(alloc(), elements);
1169
current->add(end->toInstruction());
1170
}
1171
1172
MArraySlice* ins =
1173
MArraySlice::New(alloc(), obj, begin, end, templateObj,
1174
templateObj->group()->initialHeap(constraints()));
1175
current->add(ins);
1176
current->push(ins);
1177
1178
MOZ_TRY(resumeAfter(ins));
1179
MOZ_TRY(pushTypeBarrier(ins, getInlineReturnTypeSet(), BarrierKind::TypeSet));
1180
return InliningStatus_Inlined;
1181
}
1182
1183
IonBuilder::InliningResult IonBuilder::inlineBoolean(CallInfo& callInfo) {
1184
if (callInfo.constructing()) {
1185
return InliningStatus_NotInlined;
1186
}
1187
1188
if (getInlineReturnType() != MIRType::Boolean) {
1189
return InliningStatus_NotInlined;
1190
}
1191
1192
callInfo.setImplicitlyUsedUnchecked();
1193
1194
if (callInfo.argc() > 0) {
1195
MDefinition* result = convertToBoolean(callInfo.getArg(0));
1196
current->push(result);
1197
} else {
1198
pushConstant(BooleanValue(false));
1199
}
1200
return InliningStatus_Inlined;
1201
}
1202
1203
IonBuilder::InliningResult IonBuilder::inlineNewIterator(
1204
CallInfo& callInfo, MNewIterator::Type type) {
1205
MOZ_ASSERT(!callInfo.constructing());
1206
MOZ_ASSERT(callInfo.argc() == 0);
1207
1208
JSObject* templateObject = nullptr;
1209
switch (type) {
1210
case MNewIterator::ArrayIterator:
1211
templateObject = inspector->getTemplateObjectForNative(
1212
pc, js::intrinsic_NewArrayIterator);
1213
MOZ_ASSERT_IF(templateObject, templateObject->is<ArrayIteratorObject>());
1214
break;
1215
case MNewIterator::StringIterator:
1216
templateObject = inspector->getTemplateObjectForNative(
1217
pc, js::intrinsic_NewStringIterator);
1218
MOZ_ASSERT_IF(templateObject, templateObject->is<StringIteratorObject>());
1219
break;
1220
case MNewIterator::RegExpStringIterator:
1221
templateObject = inspector->getTemplateObjectForNative(
1222
pc, js::intrinsic_NewRegExpStringIterator);
1223
MOZ_ASSERT_IF(templateObject,
1224
templateObject->is<RegExpStringIteratorObject>());
1225
break;
1226
}
1227
1228
if (!templateObject) {
1229
return InliningStatus_NotInlined;
1230
}
1231
1232
callInfo.setImplicitlyUsedUnchecked();
1233
1234
MConstant* templateConst =
1235
MConstant::NewConstraintlessObject(alloc(), templateObject);
1236
current->add(templateConst);
1237
1238
MNewIterator* ins =
1239
MNewIterator::New(alloc(), constraints(), templateConst, type);
1240
current->add(ins);
1241
current->push(ins);
1242
1243
MOZ_TRY(resumeAfter(ins));
1244
return InliningStatus_Inlined;
1245
}
1246
1247
IonBuilder::InliningResult IonBuilder::inlineArrayIteratorPrototypeOptimizable(
1248
CallInfo& callInfo) {
1249
MOZ_ASSERT(!callInfo.constructing());
1250
MOZ_ASSERT(callInfo.argc() == 0);
1251
1252
if (!ensureArrayIteratorPrototypeNextNotModified()) {
1253
return InliningStatus_NotInlined;
1254
}
1255
1256
callInfo.setImplicitlyUsedUnchecked();
1257
pushConstant(BooleanValue(true));
1258
return InliningStatus_Inlined;
1259
}
1260
1261
IonBuilder::InliningResult IonBuilder::inlineMathAbs(CallInfo& callInfo) {
1262
if (callInfo.argc() != 1 || callInfo.constructing()) {
1263
return InliningStatus_NotInlined;
1264
}
1265
1266
MIRType returnType = getInlineReturnType();
1267
MIRType argType = callInfo.getArg(0)->type();
1268
if (!IsNumberType(argType)) {
1269
return InliningStatus_NotInlined;
1270
}
1271
1272
// Either argType == returnType, or
1273
// argType == Double or Float32, returnType == Int, or
1274
// argType == Float32, returnType == Double
1275
if (argType != returnType &&
1276
!(IsFloatingPointType(argType) && returnType == MIRType::Int32) &&
1277
!(argType == MIRType::Float32 && returnType == MIRType::Double)) {
1278
return InliningStatus_NotInlined;
1279
}
1280
1281
callInfo.setImplicitlyUsedUnchecked();
1282
1283
// If the arg is a Float32, we specialize the op as double, it will be
1284
// specialized as float32 if necessary later.
1285
MIRType absType = (argType == MIRType::Float32) ? MIRType::Double : argType;
1286
MInstruction* ins = MAbs::New(alloc(), callInfo.getArg(0), absType);
1287
current->add(ins);
1288
1289
current->push(ins);
1290
return InliningStatus_Inlined;
1291
}
1292
1293
IonBuilder::InliningResult IonBuilder::inlineMathFloor(CallInfo& callInfo) {
1294
if (callInfo.argc() != 1 || callInfo.constructing()) {
1295
return InliningStatus_NotInlined;
1296
}
1297
1298
MIRType argType = callInfo.getArg(0)->type();
1299
MIRType returnType = getInlineReturnType();
1300
1301
// Math.floor(int(x)) == int(x)
1302
if (argType == MIRType::Int32 && returnType == MIRType::Int32) {
1303
callInfo.setImplicitlyUsedUnchecked();
1304
// The int operand may be something which bails out if the actual value
1305
// is not in the range of the result type of the MIR. We need to tell
1306
// the optimizer to preserve this bailout even if the final result is
1307
// fully truncated.
1308
MLimitedTruncate* ins = MLimitedTruncate::New(
1309
alloc(), callInfo.getArg(0), MDefinition::IndirectTruncate);
1310
current->add(ins);
1311
current->push(ins);
1312
return InliningStatus_Inlined;
1313
}
1314
1315
if (IsFloatingPointType(argType)) {
1316
if (returnType == MIRType::Int32) {
1317
callInfo.setImplicitlyUsedUnchecked();
1318
MFloor* ins = MFloor::New(alloc(), callInfo.getArg(0));
1319
current->add(ins);
1320
current->push(ins);
1321
return InliningStatus_Inlined;
1322
}
1323
1324
if (returnType == MIRType::Double) {
1325
callInfo.setImplicitlyUsedUnchecked();
1326
1327
MInstruction* ins = nullptr;
1328
if (MNearbyInt::HasAssemblerSupport(RoundingMode::Down)) {
1329
ins = MNearbyInt::New(alloc(), callInfo.getArg(0), argType,
1330
RoundingMode::Down);
1331
} else {
1332
ins = MMathFunction::New(alloc(), callInfo.getArg(0),
1333
MMathFunction::Floor);
1334
}
1335
1336
current->add(ins);
1337
current->push(ins);
1338
return InliningStatus_Inlined;
1339
}
1340
}
1341
1342
return InliningStatus_NotInlined;
1343
}
1344
1345
IonBuilder::InliningResult IonBuilder::inlineMathCeil(CallInfo& callInfo) {
1346
if (callInfo.argc() != 1 || callInfo.constructing()) {
1347
return InliningStatus_NotInlined;
1348
}
1349
1350
MIRType argType = callInfo.getArg(0)->type();
1351
MIRType returnType = getInlineReturnType();
1352
1353
// Math.ceil(int(x)) == int(x)
1354
if (argType == MIRType::Int32 && returnType == MIRType::Int32) {
1355
callInfo.setImplicitlyUsedUnchecked();
1356
// The int operand may be something which bails out if the actual value
1357
// is not in the range of the result type of the MIR. We need to tell
1358
// the optimizer to preserve this bailout even if the final result is
1359
// fully truncated.
1360
MLimitedTruncate* ins = MLimitedTruncate::New(
1361
alloc(), callInfo.getArg(0), MDefinition::IndirectTruncate);
1362
current->add(ins);
1363
current->push(ins);
1364
return InliningStatus_Inlined;
1365
}
1366
1367
if (IsFloatingPointType(argType)) {
1368
if (returnType == MIRType::Int32) {
1369
callInfo.setImplicitlyUsedUnchecked();
1370
MCeil* ins = MCeil::New(alloc(), callInfo.getArg(0));
1371
current->add(ins);
1372
current->push(ins);
1373
return InliningStatus_Inlined;
1374
}
1375
1376
if (returnType == MIRType::Double) {
1377
callInfo.setImplicitlyUsedUnchecked();
1378
1379
MInstruction* ins = nullptr;
1380
if (MNearbyInt::HasAssemblerSupport(RoundingMode::Up)) {
1381
ins = MNearbyInt::New(alloc(), callInfo.getArg(0), argType,
1382
RoundingMode::Up);
1383
} else {
1384
ins = MMathFunction::New(alloc(), callInfo.getArg(0),
1385
MMathFunction::Ceil);
1386
}
1387
1388
current->add(ins);
1389
current->push(ins);
1390
return InliningStatus_Inlined;
1391
}
1392
}
1393
1394
return InliningStatus_NotInlined;
1395
}
1396
1397
IonBuilder::InliningResult IonBuilder::inlineMathClz32(CallInfo& callInfo) {
1398
if (callInfo.argc() != 1 || callInfo.constructing()) {
1399
return InliningStatus_NotInlined;
1400
}
1401
1402
MIRType returnType = getInlineReturnType();
1403
if (returnType != MIRType::Int32) {
1404
return InliningStatus_NotInlined;
1405
}
1406
1407
if (!IsNumberType(callInfo.getArg(0)->type())) {
1408
return InliningStatus_NotInlined;
1409
}
1410
1411
callInfo.setImplicitlyUsedUnchecked();
1412
1413
MClz* ins = MClz::New(alloc(), callInfo.getArg(0), MIRType::Int32);
1414
current->add(ins);
1415
current->push(ins);
1416
return InliningStatus_Inlined;
1417
}
1418
1419
IonBuilder::InliningResult IonBuilder::inlineMathRound(CallInfo& callInfo) {
1420
if (callInfo.argc() != 1 || callInfo.constructing()) {
1421
return InliningStatus_NotInlined;
1422
}
1423
1424
MIRType returnType = getInlineReturnType();
1425
MIRType argType = callInfo.getArg(0)->type();
1426
1427
// Math.round(int(x)) == int(x)
1428
if (argType == MIRType::Int32 && returnType == MIRType::Int32) {
1429
callInfo.setImplicitlyUsedUnchecked();
1430
// The int operand may be something which bails out if the actual value
1431
// is not in the range of the result type of the MIR. We need to tell
1432
// the optimizer to preserve this bailout even if the final result is
1433
// fully truncated.
1434
MLimitedTruncate* ins = MLimitedTruncate::New(
1435
alloc(), callInfo.getArg(0), MDefinition::IndirectTruncate);
1436
current->add(ins);
1437
current->push(ins);
1438
return InliningStatus_Inlined;
1439
}
1440
1441
if (IsFloatingPointType(argType) && returnType == MIRType::Int32) {
1442
callInfo.setImplicitlyUsedUnchecked();
1443
MRound* ins = MRound::New(alloc(), callInfo.getArg(0));
1444
current->add(ins);
1445
current->push(ins);
1446
return InliningStatus_Inlined;
1447
}
1448
1449
if (IsFloatingPointType(argType) && returnType == MIRType::Double) {
1450
callInfo.setImplicitlyUsedUnchecked();
1451
MMathFunction* ins =
1452
MMathFunction::New(alloc(), callInfo.getArg(0), MMathFunction::Round);
1453
current->add(ins);
1454
current->push(ins);
1455
return InliningStatus_Inlined;
1456
}
1457
1458
return InliningStatus_NotInlined;
1459
}
1460
1461
IonBuilder::InliningResult IonBuilder::inlineMathSqrt(CallInfo& callInfo) {
1462
if (callInfo.argc() != 1 || callInfo.constructing()) {
1463
return InliningStatus_NotInlined;
1464
}
1465
1466
MIRType argType = callInfo.getArg(0)->type();
1467
if (getInlineReturnType() != MIRType::Double) {
1468
return InliningStatus_NotInlined;
1469
}
1470
if (!IsNumberType(argType)) {
1471
return InliningStatus_NotInlined;
1472
}
1473
1474
callInfo.setImplicitlyUsedUnchecked();
1475
1476
MSqrt* sqrt = MSqrt::New(alloc(), callInfo.getArg(0), MIRType::Double);
1477
current->add(sqrt);
1478
current->push(sqrt);
1479
return InliningStatus_Inlined;
1480
}
1481
1482
IonBuilder::InliningResult IonBuilder::inlineMathAtan2(CallInfo& callInfo) {
1483
if (callInfo.argc() != 2 || callInfo.constructing()) {
1484
return InliningStatus_NotInlined;
1485
}
1486
1487
if (getInlineReturnType() != MIRType::Double) {
1488
return InliningStatus_NotInlined;
1489
}
1490
1491
MIRType argType0 = callInfo.getArg(0)->type();
1492
MIRType argType1 = callInfo.getArg(1)->type();
1493
1494
if (!IsNumberType(argType0) || !IsNumberType(argType1)) {
1495
return InliningStatus_NotInlined;
1496
}
1497
1498
callInfo.setImplicitlyUsedUnchecked();
1499
1500
MAtan2* atan2 = MAtan2::New(alloc(), callInfo.getArg(0), callInfo.getArg(1));
1501
current->add(atan2);
1502
current->push(atan2);
1503
return InliningStatus_Inlined;
1504
}
1505
1506
IonBuilder::InliningResult IonBuilder::inlineMathHypot(CallInfo& callInfo) {
1507
if (callInfo.constructing()) {
1508
return InliningStatus_NotInlined;
1509
}
1510
1511
uint32_t argc = callInfo.argc();
1512
if (argc < 2 || argc > 4) {
1513
return InliningStatus_NotInlined;
1514
}
1515
1516
if (getInlineReturnType() != MIRType::Double) {
1517
return InliningStatus_NotInlined;
1518
}
1519
1520
MDefinitionVector vector(alloc());
1521
if (!vector.reserve(argc)) {
1522
return InliningStatus_NotInlined;
1523
}
1524
1525
for (uint32_t i = 0; i < argc; ++i) {
1526
MDefinition* arg = callInfo.getArg(i);
1527
if (!IsNumberType(arg->type())) {
1528
return InliningStatus_NotInlined;
1529
}
1530
vector.infallibleAppend(arg);
1531
}
1532
1533
callInfo.setImplicitlyUsedUnchecked();
1534
MHypot* hypot = MHypot::New(alloc(), vector);
1535
1536
if (!hypot) {
1537
return InliningStatus_NotInlined;
1538
}
1539
1540
current->add(hypot);
1541
current->push(hypot);
1542
return InliningStatus_Inlined;
1543
}
1544
1545
IonBuilder::InliningResult IonBuilder::inlineMathPow(CallInfo& callInfo) {
1546
if (callInfo.argc() != 2 || callInfo.constructing()) {
1547
return InliningStatus_NotInlined;
1548
}
1549
1550
bool emitted = false;
1551
MOZ_TRY(powTrySpecialized(&emitted, callInfo.getArg(0), callInfo.getArg(1),
1552
getInlineReturnType()));
1553
1554
if (!emitted) {
1555
return InliningStatus_NotInlined;
1556
}
1557
1558
callInfo.setImplicitlyUsedUnchecked();
1559
return InliningStatus_Inlined;
1560
}
1561
1562
IonBuilder::InliningResult IonBuilder::inlineMathRandom(CallInfo& callInfo) {
1563
if (callInfo.constructing()) {
1564
return InliningStatus_NotInlined;
1565
}
1566
1567
if (getInlineReturnType() != MIRType::Double) {
1568
return InliningStatus_NotInlined;
1569
}
1570
1571
// MRandom JIT code directly accesses the RNG. It's (barely) possible to
1572
// inline Math.random without it having been called yet, so ensure RNG
1573
// state that isn't guaranteed to be initialized already.
1574
script()->realm()->getOrCreateRandomNumberGenerator();
1575
1576
callInfo.setImplicitlyUsedUnchecked();
1577
1578
MRandom* rand = MRandom::New(alloc());
1579
current->add(rand);
1580
current->push(rand);
1581
return InliningStatus_Inlined;
1582
}
1583
1584
IonBuilder::InliningResult IonBuilder::inlineMathImul(CallInfo& callInfo) {
1585
if (callInfo.argc() != 2 || callInfo.constructing()) {
1586
return InliningStatus_NotInlined;
1587
}
1588
1589
MIRType returnType = getInlineReturnType();
1590
if (returnType != MIRType::Int32) {
1591
return InliningStatus_NotInlined;
1592
}
1593
1594
if (!IsNumberType(callInfo.getArg(0)->type())) {
1595
return InliningStatus_NotInlined;
1596
}
1597
if (!IsNumberType(callInfo.getArg(1)->type())) {
1598
return InliningStatus_NotInlined;
1599
}
1600
1601
callInfo.setImplicitlyUsedUnchecked();
1602
1603
MInstruction* first = MTruncateToInt32::New(alloc(), callInfo.getArg(0));
1604
current->add(first);
1605
1606
MInstruction* second = MTruncateToInt32::New(alloc(), callInfo.getArg(1));
1607
current->add(second);
1608
1609
MMul* ins = MMul::New(alloc(), first, second, MIRType::Int32, MMul::Integer);
1610
current->add(ins);
1611
current->push(ins);
1612
return InliningStatus_Inlined;
1613
}
1614
1615
IonBuilder::InliningResult IonBuilder::inlineMathFRound(CallInfo& callInfo) {
1616
if (callInfo.argc() != 1 || callInfo.constructing()) {
1617
return InliningStatus_NotInlined;
1618
}
1619
1620
// MIRType can't be Float32, as this point, as getInlineReturnType uses JSVal
1621
// types to infer the returned MIR type.
1622
TemporaryTypeSet* returned = getInlineReturnTypeSet();
1623
if (returned->empty()) {
1624
// As there's only one possible returned type, just add it to the observed
1625
// returned typeset
1626
returned->addType(TypeSet::DoubleType(), alloc_->lifoAlloc());
1627
} else {
1628
MIRType returnType = getInlineReturnType();
1629
if (!IsNumberType(returnType)) {
1630
return InliningStatus_NotInlined;
1631
}
1632
}
1633
1634
MIRType arg = callInfo.getArg(0)->type();
1635
if (!IsNumberType(arg)) {
1636
return InliningStatus_NotInlined;
1637
}
1638
1639
callInfo.setImplicitlyUsedUnchecked();
1640
1641
MToFloat32* ins = MToFloat32::New(alloc(), callInfo.getArg(0));
1642
current->add(ins);
1643
current->push(ins);
1644
return InliningStatus_Inlined;
1645
}
1646
1647
IonBuilder::InliningResult IonBuilder::inlineMathTrunc(CallInfo& callInfo) {
1648
if (callInfo.argc() != 1 || callInfo.constructing()) {
1649
return InliningStatus_NotInlined;
1650
}
1651
1652
MIRType argType = callInfo.getArg(0)->type();
1653
MIRType returnType = getInlineReturnType();
1654
1655
// Math.trunc(int(x)) == int(x)
1656
if (argType == MIRType::Int32 && returnType == MIRType::Int32) {
1657
callInfo.setImplicitlyUsedUnchecked();
1658
// The int operand may be something which bails out if the actual value
1659
// is not in the range of the result type of the MIR. We need to tell
1660
// the optimizer to preserve this bailout even if the final result is
1661
// fully truncated.
1662
MLimitedTruncate* ins = MLimitedTruncate::New(
1663
alloc(), callInfo.getArg(0), MDefinition::IndirectTruncate);
1664
current->add(ins);
1665
current->push(ins);
1666
return InliningStatus_Inlined;
1667
}
1668
1669
if (IsFloatingPointType(argType)) {
1670
if (returnType == MIRType::Int32) {
1671
callInfo.setImplicitlyUsedUnchecked();
1672
MTrunc* ins = MTrunc::New(alloc(), callInfo.getArg(0));
1673
current->add(ins);
1674
current->push(ins);
1675
return InliningStatus_Inlined;
1676
}
1677
1678
if (returnType == MIRType::Double) {
1679
callInfo.setImplicitlyUsedUnchecked();