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 "jit/IonBuilder.h"
8
9
#include "mozilla/DebugOnly.h"
10
#include "mozilla/ScopeExit.h"
11
12
#include "builtin/Eval.h"
13
#include "builtin/TypedObject.h"
14
#include "frontend/SourceNotes.h"
15
#include "jit/BaselineFrame.h"
16
#include "jit/BaselineInspector.h"
17
#include "jit/CacheIR.h"
18
#include "jit/Ion.h"
19
#include "jit/IonControlFlow.h"
20
#include "jit/IonOptimizationLevels.h"
21
#include "jit/JitSpewer.h"
22
#include "jit/Lowering.h"
23
#include "jit/MIRGraph.h"
24
#include "vm/ArgumentsObject.h"
25
#include "vm/BytecodeUtil.h"
26
#include "vm/EnvironmentObject.h"
27
#include "vm/Instrumentation.h"
28
#include "vm/Opcodes.h"
29
#include "vm/RegExpStatics.h"
30
#include "vm/SelfHosting.h"
31
#include "vm/TraceLogging.h"
32
33
#include "gc/Nursery-inl.h"
34
#include "jit/CompileInfo-inl.h"
35
#include "jit/shared/Lowering-shared-inl.h"
36
#include "vm/BytecodeUtil-inl.h"
37
#include "vm/EnvironmentObject-inl.h"
38
#include "vm/JSScript-inl.h"
39
#include "vm/NativeObject-inl.h"
40
#include "vm/ObjectGroup-inl.h"
41
42
using namespace js;
43
using namespace js::jit;
44
45
using mozilla::AssertedCast;
46
using mozilla::DebugOnly;
47
using mozilla::Maybe;
48
using mozilla::Nothing;
49
50
using JS::TrackedOutcome;
51
using JS::TrackedStrategy;
52
using JS::TrackedTypeSite;
53
54
class jit::BaselineFrameInspector {
55
public:
56
TypeSet::Type thisType;
57
JSObject* singletonEnvChain;
58
59
Vector<TypeSet::Type, 4, JitAllocPolicy> argTypes;
60
Vector<TypeSet::Type, 4, JitAllocPolicy> varTypes;
61
62
explicit BaselineFrameInspector(TempAllocator* temp)
63
: thisType(TypeSet::UndefinedType()),
64
singletonEnvChain(nullptr),
65
argTypes(*temp),
66
varTypes(*temp) {}
67
};
68
69
BaselineFrameInspector* jit::NewBaselineFrameInspector(TempAllocator* temp,
70
BaselineFrame* frame) {
71
MOZ_ASSERT(frame);
72
73
BaselineFrameInspector* inspector =
74
temp->lifoAlloc()->new_<BaselineFrameInspector>(temp);
75
if (!inspector) {
76
return nullptr;
77
}
78
79
// Note: copying the actual values into a temporary structure for use
80
// during compilation could capture nursery pointers, so the values' types
81
// are recorded instead.
82
83
if (frame->isFunctionFrame()) {
84
inspector->thisType =
85
TypeSet::GetMaybeUntrackedValueType(frame->thisArgument());
86
}
87
88
if (frame->environmentChain()->isSingleton()) {
89
inspector->singletonEnvChain = frame->environmentChain();
90
}
91
92
JSScript* script = frame->script();
93
94
if (script->functionNonDelazifying()) {
95
if (!inspector->argTypes.reserve(frame->numFormalArgs())) {
96
return nullptr;
97
}
98
for (size_t i = 0; i < frame->numFormalArgs(); i++) {
99
if (script->formalIsAliased(i)) {
100
inspector->argTypes.infallibleAppend(TypeSet::UndefinedType());
101
} else if (!script->argsObjAliasesFormals()) {
102
TypeSet::Type type =
103
TypeSet::GetMaybeUntrackedValueType(frame->unaliasedFormal(i));
104
inspector->argTypes.infallibleAppend(type);
105
} else if (frame->hasArgsObj()) {
106
TypeSet::Type type =
107
TypeSet::GetMaybeUntrackedValueType(frame->argsObj().arg(i));
108
inspector->argTypes.infallibleAppend(type);
109
} else {
110
inspector->argTypes.infallibleAppend(TypeSet::UndefinedType());
111
}
112
}
113
}
114
115
if (!inspector->varTypes.reserve(frame->numValueSlots())) {
116
return nullptr;
117
}
118
for (size_t i = 0; i < frame->numValueSlots(); i++) {
119
TypeSet::Type type =
120
TypeSet::GetMaybeUntrackedValueType(*frame->valueSlot(i));
121
inspector->varTypes.infallibleAppend(type);
122
}
123
124
return inspector;
125
}
126
127
IonBuilder::IonBuilder(JSContext* analysisContext, CompileRealm* realm,
128
const JitCompileOptions& options, TempAllocator* temp,
129
MIRGraph* graph, CompilerConstraintList* constraints,
130
BaselineInspector* inspector, CompileInfo* info,
131
const OptimizationInfo* optimizationInfo,
132
BaselineFrameInspector* baselineFrame,
133
size_t inliningDepth, uint32_t loopDepth)
134
: MIRGenerator(realm, options, temp, graph, info, optimizationInfo),
135
backgroundCodegen_(nullptr),
136
actionableAbortScript_(nullptr),
137
actionableAbortPc_(nullptr),
138
actionableAbortMessage_(nullptr),
139
rootList_(nullptr),
140
analysisContext(analysisContext),
141
baselineFrame_(baselineFrame),
142
constraints_(constraints),
143
tiOracle_(this, constraints),
144
thisTypes(nullptr),
145
argTypes(nullptr),
146
typeArray(nullptr),
147
typeArrayHint(0),
148
bytecodeTypeMap(nullptr),
149
current(nullptr),
150
loopDepth_(loopDepth),
151
blockWorklist(*temp),
152
cfgCurrent(nullptr),
153
cfg(nullptr),
154
trackedOptimizationSites_(*temp),
155
lexicalCheck_(nullptr),
156
callerResumePoint_(nullptr),
157
callerBuilder_(nullptr),
158
iterators_(*temp),
159
loopHeaders_(*temp),
160
loopHeaderStack_(*temp),
161
#ifdef DEBUG
162
cfgLoopHeaderStack_(*temp),
163
#endif
164
inspector(inspector),
165
inliningDepth_(inliningDepth),
166
inlinedBytecodeLength_(0),
167
numLoopRestarts_(0),
168
failedBoundsCheck_(info->script()->failedBoundsCheck()),
169
failedShapeGuard_(info->script()->failedShapeGuard()),
170
failedLexicalCheck_(info->script()->failedLexicalCheck()),
171
#ifdef DEBUG
172
hasLazyArguments_(false),
173
#endif
174
inlineCallInfo_(nullptr),
175
maybeFallbackFunctionGetter_(nullptr) {
176
script_ = info->script();
177
scriptHasIonScript_ = script_->hasIonScript();
178
pc = info->startPC();
179
180
// The script must have a JitScript. Compilation requires a BaselineScript
181
// too.
182
MOZ_ASSERT(script_->hasJitScript());
183
MOZ_ASSERT_IF(!info->isAnalysis(), script_->hasBaselineScript());
184
185
MOZ_ASSERT(!!analysisContext ==
186
(info->analysisMode() == Analysis_DefiniteProperties));
187
MOZ_ASSERT(script_->numBytecodeTypeSets() < JSScript::MaxBytecodeTypeSets);
188
189
if (!info->isAnalysis()) {
190
script()->jitScript()->setIonCompiledOrInlined();
191
}
192
}
193
194
void IonBuilder::clearForBackEnd() {
195
MOZ_ASSERT(!analysisContext);
196
baselineFrame_ = nullptr;
197
}
198
199
mozilla::GenericErrorResult<AbortReason> IonBuilder::abort(AbortReason r) {
200
auto res = this->MIRGenerator::abort(r);
201
#ifdef DEBUG
202
JitSpew(JitSpew_IonAbort, "aborted @ %s:%d", script()->filename(),
203
PCToLineNumber(script(), pc));
204
#else
205
JitSpew(JitSpew_IonAbort, "aborted @ %s", script()->filename());
206
#endif
207
return res;
208
}
209
210
mozilla::GenericErrorResult<AbortReason> IonBuilder::abort(AbortReason r,
211
const char* message,
212
...) {
213
// Don't call PCToLineNumber in release builds.
214
va_list ap;
215
va_start(ap, message);
216
auto res = this->MIRGenerator::abortFmt(r, message, ap);
217
va_end(ap);
218
#ifdef DEBUG
219
JitSpew(JitSpew_IonAbort, "aborted @ %s:%d", script()->filename(),
220
PCToLineNumber(script(), pc));
221
#else
222
JitSpew(JitSpew_IonAbort, "aborted @ %s", script()->filename());
223
#endif
224
trackActionableAbort(message);
225
return res;
226
}
227
228
IonBuilder* IonBuilder::outermostBuilder() {
229
IonBuilder* builder = this;
230
while (builder->callerBuilder_) {
231
builder = builder->callerBuilder_;
232
}
233
return builder;
234
}
235
236
void IonBuilder::trackActionableAbort(const char* message) {
237
if (!isOptimizationTrackingEnabled()) {
238
return;
239
}
240
241
IonBuilder* topBuilder = outermostBuilder();
242
if (topBuilder->hadActionableAbort()) {
243
return;
244
}
245
246
topBuilder->actionableAbortScript_ = script();
247
topBuilder->actionableAbortPc_ = pc;
248
topBuilder->actionableAbortMessage_ = message;
249
}
250
251
void IonBuilder::spew(const char* message) {
252
// Don't call PCToLineNumber in release builds.
253
#ifdef DEBUG
254
JitSpew(JitSpew_IonMIR, "%s @ %s:%d", message, script()->filename(),
255
PCToLineNumber(script(), pc));
256
#endif
257
}
258
259
JSFunction* IonBuilder::getSingleCallTarget(TemporaryTypeSet* calleeTypes) {
260
if (!calleeTypes) {
261
return nullptr;
262
}
263
264
TemporaryTypeSet::ObjectKey* key = calleeTypes->maybeSingleObject();
265
if (!key || key->clasp() != &JSFunction::class_) {
266
return nullptr;
267
}
268
269
if (key->isSingleton()) {
270
return &key->singleton()->as<JSFunction>();
271
}
272
273
if (JSFunction* fun = key->group()->maybeInterpretedFunction()) {
274
return fun;
275
}
276
277
return nullptr;
278
}
279
280
AbortReasonOr<Ok> IonBuilder::getPolyCallTargets(TemporaryTypeSet* calleeTypes,
281
bool constructing,
282
InliningTargets& targets,
283
uint32_t maxTargets) {
284
MOZ_ASSERT(targets.empty());
285
286
if (!calleeTypes) {
287
return Ok();
288
}
289
290
if (calleeTypes->baseFlags() != 0) {
291
return Ok();
292
}
293
294
unsigned objCount = calleeTypes->getObjectCount();
295
296
if (objCount == 0 || objCount > maxTargets) {
297
return Ok();
298
}
299
300
if (!targets.reserve(objCount)) {
301
return abort(AbortReason::Alloc);
302
}
303
for (unsigned i = 0; i < objCount; i++) {
304
JSObject* obj = calleeTypes->getSingleton(i);
305
ObjectGroup* group = nullptr;
306
if (obj) {
307
MOZ_ASSERT(obj->isSingleton());
308
} else {
309
group = calleeTypes->getGroup(i);
310
if (!group) {
311
continue;
312
}
313
314
obj = group->maybeInterpretedFunction();
315
if (!obj) {
316
targets.clear();
317
return Ok();
318
}
319
320
MOZ_ASSERT(!obj->isSingleton());
321
}
322
323
// Don't optimize if the callee is not callable or constructable per
324
// the manner it is being invoked, so that CallKnown does not have to
325
// handle these cases (they will always throw).
326
if (constructing ? !obj->isConstructor() : !obj->isCallable()) {
327
targets.clear();
328
return Ok();
329
}
330
331
targets.infallibleAppend(InliningTarget(obj, group));
332
}
333
334
return Ok();
335
}
336
337
IonBuilder::InliningDecision IonBuilder::DontInline(JSScript* targetScript,
338
const char* reason) {
339
if (targetScript) {
340
JitSpew(JitSpew_Inlining, "Cannot inline %s:%u:%u %s",
341
targetScript->filename(), targetScript->lineno(),
342
targetScript->column(), reason);
343
} else {
344
JitSpew(JitSpew_Inlining, "Cannot inline: %s", reason);
345
}
346
347
return InliningDecision_DontInline;
348
}
349
350
/*
351
* |hasCommonInliningPath| determines whether the current inlining path has been
352
* seen before based on the sequence of scripts in the chain of |IonBuilder|s.
353
*
354
* An inlining path for a function |f| is the sequence of functions whose
355
* inlinings precede |f| up to any previous occurrences of |f|.
356
* So, if we have the chain of inlinings
357
*
358
* f1 -> f2 -> f -> f3 -> f4 -> f5 -> f
359
* -------- --------------
360
*
361
* the inlining paths for |f| are [f2, f1] and [f5, f4, f3].
362
* When attempting to inline |f|, we find all existing inlining paths for |f|
363
* and check whether they share a common prefix with the path created were |f|
364
* inlined.
365
*
366
* For example, given mutually recursive functions |f| and |g|, a possible
367
* inlining is
368
*
369
* +---- Inlining stopped here...
370
* |
371
* v
372
* a -> f -> g -> f \ -> g -> f -> g -> ...
373
*
374
* where the vertical bar denotes the termination of inlining.
375
* Inlining is terminated because we have already observed the inlining path
376
* [f] when inlining function |g|. Note that this will inline recursive
377
* functions such as |fib| only one level, as |fib| has a zero length inlining
378
* path which trivially prefixes all inlining paths.
379
*
380
*/
381
bool IonBuilder::hasCommonInliningPath(const JSScript* scriptToInline) {
382
// Find all previous inlinings of the |scriptToInline| and check for common
383
// inlining paths with the top of the inlining stack.
384
for (IonBuilder* it = this->callerBuilder_; it; it = it->callerBuilder_) {
385
if (it->script() != scriptToInline) {
386
continue;
387
}
388
389
// This only needs to check the top of each stack for a match,
390
// as a match of length one ensures a common prefix.
391
IonBuilder* path = it->callerBuilder_;
392
if (!path || this->script() == path->script()) {
393
return true;
394
}
395
}
396
397
return false;
398
}
399
400
IonBuilder::InliningDecision IonBuilder::canInlineTarget(JSFunction* target,
401
CallInfo& callInfo) {
402
if (!optimizationInfo().inlineInterpreted()) {
403
trackOptimizationOutcome(TrackedOutcome::CantInlineGeneric);
404
return InliningDecision_DontInline;
405
}
406
407
if (TraceLogTextIdEnabled(TraceLogger_InlinedScripts)) {
408
return DontInline(nullptr,
409
"Tracelogging of inlined scripts is enabled"
410
"but Tracelogger cannot do that yet.");
411
}
412
413
if (!target->isInterpreted()) {
414
trackOptimizationOutcome(TrackedOutcome::CantInlineNotInterpreted);
415
return DontInline(nullptr, "Non-interpreted target");
416
}
417
418
// Never inline scripted cross-realm calls.
419
if (target->realm() != script()->realm()) {
420
trackOptimizationOutcome(TrackedOutcome::CantInlineCrossRealm);
421
return DontInline(nullptr, "Cross-realm call");
422
}
423
424
if (info().analysisMode() != Analysis_DefiniteProperties) {
425
// If |this| or an argument has an empty resultTypeSet, don't bother
426
// inlining, as the call is currently unreachable due to incomplete type
427
// information. This does not apply to the definite properties analysis,
428
// in that case we want to inline anyway.
429
430
if (callInfo.thisArg()->emptyResultTypeSet()) {
431
trackOptimizationOutcome(TrackedOutcome::CantInlineUnreachable);
432
return DontInline(nullptr, "Empty TypeSet for |this|");
433
}
434
435
for (size_t i = 0; i < callInfo.argc(); i++) {
436
if (callInfo.getArg(i)->emptyResultTypeSet()) {
437
trackOptimizationOutcome(TrackedOutcome::CantInlineUnreachable);
438
return DontInline(nullptr, "Empty TypeSet for argument");
439
}
440
}
441
}
442
443
// Allow constructing lazy scripts when performing the definite properties
444
// analysis, as baseline has not been used to warm the caller up yet.
445
if (target->isInterpreted() &&
446
info().analysisMode() == Analysis_DefiniteProperties) {
447
RootedFunction fun(analysisContext, target);
448
RootedScript script(analysisContext,
449
JSFunction::getOrCreateScript(analysisContext, fun));
450
if (!script) {
451
return InliningDecision_Error;
452
}
453
454
if (CanBaselineInterpretScript(script)) {
455
AutoKeepJitScripts keepJitScript(analysisContext);
456
if (!script->ensureHasJitScript(analysisContext, keepJitScript)) {
457
return InliningDecision_Error;
458
}
459
}
460
}
461
462
if (!target->hasScript()) {
463
trackOptimizationOutcome(TrackedOutcome::CantInlineLazy);
464
return DontInline(nullptr, "Lazy script");
465
}
466
467
JSScript* inlineScript = target->nonLazyScript();
468
if (callInfo.constructing() && !target->isConstructor()) {
469
trackOptimizationOutcome(TrackedOutcome::CantInlineNotConstructor);
470
return DontInline(inlineScript, "Callee is not a constructor");
471
}
472
473
if (!callInfo.constructing() && target->isClassConstructor()) {
474
trackOptimizationOutcome(TrackedOutcome::CantInlineClassConstructor);
475
return DontInline(inlineScript, "Not constructing class constructor");
476
}
477
478
if (!CanIonInlineScript(inlineScript)) {
479
trackOptimizationOutcome(TrackedOutcome::CantInlineDisabledIon);
480
return DontInline(inlineScript, "Disabled Ion compilation");
481
}
482
483
if (info().isAnalysis()) {
484
// Analysis requires only a JitScript.
485
if (!inlineScript->hasJitScript()) {
486
trackOptimizationOutcome(TrackedOutcome::CantInlineNoJitScript);
487
return DontInline(inlineScript, "No JitScript");
488
}
489
} else {
490
// Compilation requires a BaselineScript.
491
if (!inlineScript->hasBaselineScript()) {
492
trackOptimizationOutcome(TrackedOutcome::CantInlineNoBaseline);
493
return DontInline(inlineScript, "No baseline jitcode");
494
}
495
}
496
497
// Don't inline functions with a higher optimization level.
498
if (!isHighestOptimizationLevel()) {
499
OptimizationLevel level = optimizationLevel();
500
if (inlineScript->hasIonScript() &&
501
(inlineScript->ionScript()->isRecompiling() ||
502
inlineScript->ionScript()->optimizationLevel() > level)) {
503
return DontInline(inlineScript, "More optimized");
504
}
505
if (IonOptimizations.levelForScript(inlineScript, nullptr) > level) {
506
return DontInline(inlineScript, "Should be more optimized");
507
}
508
}
509
510
if (TooManyFormalArguments(target->nargs())) {
511
trackOptimizationOutcome(TrackedOutcome::CantInlineTooManyArgs);
512
return DontInline(inlineScript, "Too many args");
513
}
514
515
// We check the number of actual arguments against the maximum number of
516
// formal arguments as we do not want to encode all actual arguments in the
517
// callerResumePoint.
518
if (TooManyFormalArguments(callInfo.argc())) {
519
trackOptimizationOutcome(TrackedOutcome::CantInlineTooManyArgs);
520
return DontInline(inlineScript, "Too many actual args");
521
}
522
523
if (hasCommonInliningPath(inlineScript)) {
524
trackOptimizationOutcome(TrackedOutcome::HasCommonInliningPath);
525
return DontInline(inlineScript, "Common inlining path");
526
}
527
528
if (inlineScript->uninlineable()) {
529
trackOptimizationOutcome(TrackedOutcome::CantInlineGeneric);
530
return DontInline(inlineScript, "Uninlineable script");
531
}
532
533
if (inlineScript->needsArgsObj()) {
534
trackOptimizationOutcome(TrackedOutcome::CantInlineNeedsArgsObj);
535
return DontInline(inlineScript, "Script that needs an arguments object");
536
}
537
538
if (inlineScript->isDebuggee()) {
539
trackOptimizationOutcome(TrackedOutcome::CantInlineDebuggee);
540
return DontInline(inlineScript, "Script is debuggee");
541
}
542
543
return InliningDecision_Inline;
544
}
545
546
AbortReasonOr<Ok> IonBuilder::analyzeNewLoopTypes(
547
const CFGBlock* loopEntryBlock) {
548
CFGLoopEntry* loopEntry = loopEntryBlock->stopIns()->toLoopEntry();
549
CFGBlock* cfgBlock = loopEntry->successor();
550
MBasicBlock* entry = blockWorklist[cfgBlock->id()];
551
MOZ_ASSERT(!entry->isDead());
552
553
// The phi inputs at the loop head only reflect types for variables that
554
// were present at the start of the loop. If the variable changes to a new
555
// type within the loop body, and that type is carried around to the loop
556
// head, then we need to know about the new type up front.
557
//
558
// Since SSA information hasn't been constructed for the loop body yet, we
559
// need a separate analysis to pick out the types that might flow around
560
// the loop header. This is a best-effort analysis that may either over-
561
// or under-approximate the set of such types.
562
//
563
// Over-approximating the types may lead to inefficient generated code, and
564
// under-approximating the types will cause the loop body to be analyzed
565
// multiple times as the correct types are deduced (see finishLoop).
566
567
// If we restarted processing of an outer loop then get loop header types
568
// directly from the last time we have previously processed this loop. This
569
// both avoids repeated work from the bytecode traverse below, and will
570
// also pick up types discovered while previously building the loop body.
571
bool foundEntry = false;
572
for (size_t i = 0; i < loopHeaders_.length(); i++) {
573
if (loopHeaders_[i].pc == cfgBlock->startPc()) {
574
MBasicBlock* oldEntry = loopHeaders_[i].header;
575
576
// If this block has been discarded, its resume points will have
577
// already discarded their operands.
578
if (oldEntry->isDead()) {
579
loopHeaders_[i].header = entry;
580
foundEntry = true;
581
break;
582
}
583
584
MResumePoint* oldEntryRp = oldEntry->entryResumePoint();
585
size_t stackDepth = oldEntryRp->stackDepth();
586
for (size_t slot = 0; slot < stackDepth; slot++) {
587
MDefinition* oldDef = oldEntryRp->getOperand(slot);
588
if (!oldDef->isPhi()) {
589
MOZ_ASSERT(oldDef->block()->id() < oldEntry->id());
590
MOZ_ASSERT(oldDef == entry->getSlot(slot));
591
continue;
592
}
593
MPhi* oldPhi = oldDef->toPhi();
594
MPhi* newPhi = entry->getSlot(slot)->toPhi();
595
if (!newPhi->addBackedgeType(alloc(), oldPhi->type(),
596
oldPhi->resultTypeSet())) {
597
return abort(AbortReason::Alloc);
598
}
599
}
600
601
// Update the most recent header for this loop encountered, in case
602
// new types flow to the phis and the loop is processed at least
603
// three times.
604
loopHeaders_[i].header = entry;
605
return Ok();
606
}
607
}
608
if (!foundEntry) {
609
if (!loopHeaders_.append(LoopHeader(cfgBlock->startPc(), entry))) {
610
return abort(AbortReason::Alloc);
611
}
612
}
613
614
if (loopEntry->isForIn()) {
615
// The backedge will have MIteratorMore with MIRType::Value. This slot
616
// is initialized to MIRType::Undefined before the loop. Add
617
// MIRType::Value to avoid unnecessary loop restarts.
618
619
MPhi* phi = entry->getSlot(entry->stackDepth() - 1)->toPhi();
620
MOZ_ASSERT(phi->getOperand(0)->type() == MIRType::Undefined);
621
622
if (!phi->addBackedgeType(alloc(), MIRType::Value, nullptr)) {
623
return abort(AbortReason::Alloc);
624
}
625
}
626
627
// Get the start and end pc of this loop.
628
jsbytecode* start = loopEntryBlock->stopPc();
629
start += GetBytecodeLength(start);
630
jsbytecode* end = loopEntry->loopStopPc();
631
632
// Iterate the bytecode quickly to seed possible types in the loopheader.
633
jsbytecode* last = nullptr;
634
jsbytecode* earlier = nullptr;
635
for (jsbytecode* pc = start; pc != end;
636
earlier = last, last = pc, pc += GetBytecodeLength(pc)) {
637
uint32_t slot;
638
if (*pc == JSOP_SETLOCAL) {
639
slot = info().localSlot(GET_LOCALNO(pc));
640
} else if (*pc == JSOP_SETARG) {
641
slot = info().argSlotUnchecked(GET_ARGNO(pc));
642
} else {
643
continue;
644
}
645
if (slot >= info().firstStackSlot()) {
646
continue;
647
}
648
if (!last) {
649
continue;
650
}
651
652
MPhi* phi = entry->getSlot(slot)->toPhi();
653
654
if (*last == JSOP_POS || *last == JSOP_TONUMERIC) {
655
last = earlier;
656
}
657
658
if (BytecodeOpHasTypeSet(JSOp(*last))) {
659
TemporaryTypeSet* typeSet = bytecodeTypes(last);
660
if (!typeSet->empty()) {
661
MIRType type = typeSet->getKnownMIRType();
662
if (!phi->addBackedgeType(alloc(), type, typeSet)) {
663
return abort(AbortReason::Alloc);
664
}
665
}
666
} else if (*last == JSOP_GETLOCAL || *last == JSOP_GETARG) {
667
uint32_t slot = (*last == JSOP_GETLOCAL)
668
? info().localSlot(GET_LOCALNO(last))
669
: info().argSlotUnchecked(GET_ARGNO(last));
670
if (slot < info().firstStackSlot()) {
671
MPhi* otherPhi = entry->getSlot(slot)->toPhi();
672
if (otherPhi->hasBackedgeType()) {
673
if (!phi->addBackedgeType(alloc(), otherPhi->type(),
674
otherPhi->resultTypeSet())) {
675
return abort(AbortReason::Alloc);
676
}
677
}
678
}
679
} else {
680
MIRType type = MIRType::None;
681
switch (*last) {
682
case JSOP_VOID:
683
case JSOP_UNDEFINED:
684
type = MIRType::Undefined;
685
break;
686
case JSOP_GIMPLICITTHIS:
687
if (!script()->hasNonSyntacticScope()) {
688
type = MIRType::Undefined;
689
}
690
break;
691
case JSOP_NULL:
692
type = MIRType::Null;
693
break;
694
case JSOP_ZERO:
695
case JSOP_ONE:
696
case JSOP_INT8:
697
case JSOP_INT32:
698
case JSOP_UINT16:
699
case JSOP_UINT24:
700
case JSOP_RESUMEINDEX:
701
case JSOP_BITAND:
702
case JSOP_BITOR:
703
case JSOP_BITXOR:
704
case JSOP_BITNOT:
705
case JSOP_RSH:
706
case JSOP_LSH:
707
case JSOP_URSH:
708
type = MIRType::Int32;
709
break;
710
case JSOP_FALSE:
711
case JSOP_TRUE:
712
case JSOP_EQ:
713
case JSOP_NE:
714
case JSOP_LT:
715
case JSOP_LE:
716
case JSOP_GT:
717
case JSOP_GE:
718
case JSOP_NOT:
719
case JSOP_STRICTEQ:
720
case JSOP_STRICTNE:
721
case JSOP_IN:
722
case JSOP_INSTANCEOF:
723
case JSOP_HASOWN:
724
type = MIRType::Boolean;
725
break;
726
case JSOP_DOUBLE:
727
type = MIRType::Double;
728
break;
729
case JSOP_ITERNEXT:
730
case JSOP_STRING:
731
case JSOP_TOSTRING:
732
case JSOP_TYPEOF:
733
case JSOP_TYPEOFEXPR:
734
type = MIRType::String;
735
break;
736
case JSOP_SYMBOL:
737
type = MIRType::Symbol;
738
break;
739
case JSOP_ADD:
740
case JSOP_SUB:
741
case JSOP_MUL:
742
case JSOP_DIV:
743
case JSOP_MOD:
744
case JSOP_NEG:
745
case JSOP_INC:
746
case JSOP_DEC:
747
type = inspector->expectedResultType(last);
748
break;
749
case JSOP_BIGINT:
750
type = MIRType::BigInt;
751
break;
752
default:
753
break;
754
}
755
if (type != MIRType::None) {
756
if (!phi->addBackedgeType(alloc(), type, nullptr)) {
757
return abort(AbortReason::Alloc);
758
}
759
}
760
}
761
}
762
return Ok();
763
}
764
765
AbortReasonOr<Ok> IonBuilder::init() {
766
{
767
LifoAlloc::AutoFallibleScope fallibleAllocator(alloc().lifoAlloc());
768
if (!JitScript::FreezeTypeSets(constraints(), script(), &thisTypes,
769
&argTypes, &typeArray)) {
770
return abort(AbortReason::Alloc);
771
}
772
}
773
774
if (!alloc().ensureBallast()) {
775
return abort(AbortReason::Alloc);
776
}
777
778
{
779
JSContext* cx = TlsContext.get();
780
RootedScript rootedScript(cx, script());
781
if (!rootedScript->jitScript()->ensureHasCachedIonData(cx, rootedScript)) {
782
return abort(AbortReason::Error);
783
}
784
}
785
786
if (inlineCallInfo_) {
787
// If we're inlining, the actual this/argument types are not necessarily
788
// a subset of the script's observed types. |argTypes| is never accessed
789
// for inlined scripts, so we just null it.
790
thisTypes = inlineCallInfo_->thisArg()->resultTypeSet();
791
argTypes = nullptr;
792
}
793
794
bytecodeTypeMap = script()->jitScript()->bytecodeTypeMap();
795
796
return Ok();
797
}
798
799
AbortReasonOr<Ok> IonBuilder::build() {
800
// Spew IC info for inlined script, but only when actually compiling,
801
// not when analyzing it.
802
#ifdef JS_STRUCTURED_SPEW
803
if (!info().isAnalysis()) {
804
JitSpewBaselineICStats(script(), "To-Be-Compiled");
805
}
806
#endif
807
808
MOZ_TRY(init());
809
810
// The JitScript-based inlining heuristics only affect the highest
811
// optimization level. Other levels do almost no inlining and we don't want to
812
// overwrite data from the highest optimization tier.
813
if (isHighestOptimizationLevel()) {
814
script()->jitScript()->resetMaxInliningDepth();
815
}
816
817
MBasicBlock* entry;
818
MOZ_TRY_VAR(entry, newBlock(info().firstStackSlot(), pc));
819
MOZ_TRY(setCurrentAndSpecializePhis(entry));
820
821
#ifdef JS_JITSPEW
822
if (info().isAnalysis()) {
823
JitSpew(JitSpew_IonScripts, "Analyzing script %s:%u:%u (%p) %s",
824
script()->filename(), script()->lineno(), script()->column(),
825
(void*)script(), AnalysisModeString(info().analysisMode()));
826
} else {
827
JitSpew(JitSpew_IonScripts,
828
"%sompiling script %s:%u:%u (%p) (warmup-counter=%" PRIu32
829
", level=%s)",
830
(script()->hasIonScript() ? "Rec" : "C"), script()->filename(),
831
script()->lineno(), script()->column(), (void*)script(),
832
script()->getWarmUpCount(),
833
OptimizationLevelString(optimizationLevel()));
834
}
835
#endif
836
837
MOZ_TRY(initParameters());
838
initLocals();
839
840
// Initialize something for the env chain. We can bail out before the
841
// start instruction, but the snapshot is encoded *at* the start
842
// instruction, which means generating any code that could load into
843
// registers is illegal.
844
MInstruction* env = MConstant::New(alloc(), UndefinedValue());
845
current->add(env);
846
current->initSlot(info().environmentChainSlot(), env);
847
848
// Initialize the return value.
849
MInstruction* returnValue = MConstant::New(alloc(), UndefinedValue());
850
current->add(returnValue);
851
current->initSlot(info().returnValueSlot(), returnValue);
852
853
// Initialize the arguments object slot to undefined if necessary.
854
if (info().hasArguments()) {
855
MInstruction* argsObj = MConstant::New(alloc(), UndefinedValue());
856
current->add(argsObj);
857
current->initSlot(info().argsObjSlot(), argsObj);
858
}
859
860
// Emit the start instruction, so we can begin real instructions.
861
current->add(MStart::New(alloc()));
862
863
// Guard against over-recursion. Do this before we start unboxing, since
864
// this will create an OSI point that will read the incoming argument
865
// values, which is nice to do before their last real use, to minimize
866
// register/stack pressure.
867
MCheckOverRecursed* check = MCheckOverRecursed::New(alloc());
868
current->add(check);
869
MResumePoint* entryRpCopy =
870
MResumePoint::Copy(alloc(), current->entryResumePoint());
871
if (!entryRpCopy) {
872
return abort(AbortReason::Alloc);
873
}
874
check->setResumePoint(entryRpCopy);
875
876
// Parameters have been checked to correspond to the typeset, now we unbox
877
// what we can in an infallible manner.
878
MOZ_TRY(rewriteParameters());
879
880
// Check for redeclaration errors for global scripts.
881
if (!info().funMaybeLazy() && !info().module() &&
882
script()->bodyScope()->is<GlobalScope>() &&
883
script()->bodyScope()->as<GlobalScope>().hasBindings()) {
884
MGlobalNameConflictsCheck* redeclCheck =
885
MGlobalNameConflictsCheck::New(alloc());
886
current->add(redeclCheck);
887
MResumePoint* entryRpCopy =
888
MResumePoint::Copy(alloc(), current->entryResumePoint());
889
if (!entryRpCopy) {
890
return abort(AbortReason::Alloc);
891
}
892
redeclCheck->setResumePoint(entryRpCopy);
893
}
894
895
// It's safe to start emitting actual IR, so now build the env chain.
896
MOZ_TRY(initEnvironmentChain());
897
if (info().needsArgsObj()) {
898
initArgumentsObject();
899
}
900
901
// The type analysis phase attempts to insert unbox operations near
902
// definitions of values. It also attempts to replace uses in resume points
903
// with the narrower, unboxed variants. However, we must prevent this
904
// replacement from happening on values in the entry snapshot. Otherwise we
905
// could get this:
906
//
907
// v0 = MParameter(0)
908
// v1 = MParameter(1)
909
// -- ResumePoint(v2, v3)
910
// v2 = Unbox(v0, INT32)
911
// v3 = Unbox(v1, INT32)
912
//
913
// So we attach the initial resume point to each parameter, which the type
914
// analysis explicitly checks (this is the same mechanism used for
915
// effectful operations).
916
for (uint32_t i = 0; i < info().endArgSlot(); i++) {
917
MInstruction* ins = current->getEntrySlot(i)->toInstruction();
918
if (ins->type() != MIRType::Value) {
919
continue;
920
}
921
922
MResumePoint* entryRpCopy =
923
MResumePoint::Copy(alloc(), current->entryResumePoint());
924
if (!entryRpCopy) {
925
return abort(AbortReason::Alloc);
926
}
927
ins->setResumePoint(entryRpCopy);
928
}
929
930
#ifdef DEBUG
931
// lazyArguments should never be accessed in |argsObjAliasesFormals| scripts.
932
if (info().hasArguments() && !info().argsObjAliasesFormals()) {
933
hasLazyArguments_ = true;
934
}
935
#endif
936
937
insertRecompileCheck();
938
939
auto clearLastPriorResumePoint = mozilla::MakeScopeExit([&] {
940
// Discard unreferenced & pre-allocated resume points.
941
replaceMaybeFallbackFunctionGetter(nullptr);
942
});
943
944
MOZ_TRY(traverseBytecode());
945
946
if (isHighestOptimizationLevel() &&
947
inlinedBytecodeLength_ > script_->jitScript()->inlinedBytecodeLength()) {
948
script_->jitScript()->setInlinedBytecodeLength(inlinedBytecodeLength_);
949
}
950
951
MOZ_TRY(maybeAddOsrTypeBarriers());
952
MOZ_TRY(processIterators());
953
954
if (!info().isAnalysis() && !abortedPreliminaryGroups().empty()) {
955
return abort(AbortReason::PreliminaryObjects);
956
}
957
958
MOZ_ASSERT(loopDepth_ == 0);
959
return Ok();
960
}
961
962
AbortReasonOr<Ok> IonBuilder::processIterators() {
963
// Find and mark phis that must transitively hold an iterator live.
964
965
Vector<MDefinition*, 8, SystemAllocPolicy> worklist;
966
967
for (size_t i = 0; i < iterators_.length(); i++) {
968
MDefinition* iter = iterators_[i];
969
if (!iter->isInWorklist()) {
970
if (!worklist.append(iter)) {
971
return abort(AbortReason::Alloc);
972
}
973
iter->setInWorklist();
974
}
975
}
976
977
while (!worklist.empty()) {
978
MDefinition* def = worklist.popCopy();
979
def->setNotInWorklist();
980
981
if (def->isPhi()) {
982
MPhi* phi = def->toPhi();
983
phi->setIterator();
984
phi->setImplicitlyUsedUnchecked();
985
}
986
987
for (MUseDefIterator iter(def); iter; iter++) {
988
MDefinition* use = iter.def();
989
if (!use->isInWorklist() &&
990
(!use->isPhi() || !use->toPhi()->isIterator())) {
991
if (!worklist.append(use)) {
992
return abort(AbortReason::Alloc);
993
}
994
use->setInWorklist();
995
}
996
}
997
}
998
999
return Ok();
1000
}
1001
1002
AbortReasonOr<Ok> IonBuilder::buildInline(IonBuilder* callerBuilder,
1003
MResumePoint* callerResumePoint,
1004
CallInfo& callInfo) {
1005
inlineCallInfo_ = &callInfo;
1006
1007
// Spew IC info for inlined script, but only when actually compiling,
1008
// not when analyzing it.
1009
#ifdef JS_STRUCTURED_SPEW
1010
if (!info().isAnalysis()) {
1011
JitSpewBaselineICStats(script(), "To-Be-Inlined");
1012
}
1013
#endif
1014
1015
MOZ_TRY(init());
1016
1017
JitSpew(JitSpew_IonScripts, "Inlining script %s:%u:%u (%p)",
1018
script()->filename(), script()->lineno(), script()->column(),
1019
(void*)script());
1020
1021
callerBuilder_ = callerBuilder;
1022
callerResumePoint_ = callerResumePoint;
1023
1024
if (callerBuilder->failedBoundsCheck_) {
1025
failedBoundsCheck_ = true;
1026
}
1027
1028
if (callerBuilder->failedShapeGuard_) {
1029
failedShapeGuard_ = true;
1030
}
1031
1032
if (callerBuilder->failedLexicalCheck_) {
1033
failedLexicalCheck_ = true;
1034
}
1035
1036
safeForMinorGC_ = callerBuilder->safeForMinorGC_;
1037
1038
// Generate single entrance block.
1039
MBasicBlock* entry;
1040
MOZ_TRY_VAR(entry, newBlock(info().firstStackSlot(), pc));
1041
MOZ_TRY(setCurrentAndSpecializePhis(entry));
1042
1043
current->setCallerResumePoint(callerResumePoint);
1044
1045
// Connect the entrance block to the last block in the caller's graph.
1046
MBasicBlock* predecessor = callerBuilder->current;
1047
MOZ_ASSERT(predecessor == callerResumePoint->block());
1048
1049
predecessor->end(MGoto::New(alloc(), current));
1050
if (!current->addPredecessorWithoutPhis(predecessor)) {
1051
return abort(AbortReason::Alloc);
1052
}
1053
1054
// Initialize env chain slot to Undefined. It's set later by
1055
// |initEnvironmentChain|.
1056
MInstruction* env = MConstant::New(alloc(), UndefinedValue());
1057
current->add(env);
1058
current->initSlot(info().environmentChainSlot(), env);
1059
1060
// Initialize |return value| slot.
1061
MInstruction* returnValue = MConstant::New(alloc(), UndefinedValue());
1062
current->add(returnValue);
1063
current->initSlot(info().returnValueSlot(), returnValue);
1064
1065
// Initialize |arguments| slot.
1066
if (info().hasArguments()) {
1067
MInstruction* argsObj = MConstant::New(alloc(), UndefinedValue());
1068
current->add(argsObj);
1069
current->initSlot(info().argsObjSlot(), argsObj);
1070
}
1071
1072
// Initialize |this| slot.
1073
current->initSlot(info().thisSlot(), callInfo.thisArg());
1074
1075
JitSpew(JitSpew_Inlining, "Initializing %u arg slots", info().nargs());
1076
1077
// NB: Ion does not inline functions which |needsArgsObj|. So using argSlot()
1078
// instead of argSlotUnchecked() below is OK
1079
MOZ_ASSERT(!info().needsArgsObj());
1080
1081
// Initialize actually set arguments.
1082
uint32_t existing_args = Min<uint32_t>(callInfo.argc(), info().nargs());
1083
for (size_t i = 0; i < existing_args; ++i) {
1084
MDefinition* arg = callInfo.getArg(i);
1085
current->initSlot(info().argSlot(i), arg);
1086
}
1087
1088
// Pass Undefined for missing arguments
1089
for (size_t i = callInfo.argc(); i < info().nargs(); ++i) {
1090
MConstant* arg = MConstant::New(alloc(), UndefinedValue());
1091
current->add(arg);
1092
current->initSlot(info().argSlot(i), arg);
1093
}
1094
1095
JitSpew(JitSpew_Inlining, "Initializing %u locals", info().nlocals());
1096
1097
initLocals();
1098
1099
JitSpew(JitSpew_Inlining,
1100
"Inline entry block MResumePoint %p, %u stack slots",
1101
(void*)current->entryResumePoint(),
1102
current->entryResumePoint()->stackDepth());
1103
1104
// +2 for the env chain and |this|, maybe another +1 for arguments object
1105
// slot.
1106
MOZ_ASSERT(current->entryResumePoint()->stackDepth() == info().totalSlots());
1107
1108
#ifdef DEBUG
1109
if (script_->argumentsHasVarBinding()) {
1110
hasLazyArguments_ = true;
1111
}
1112
#endif
1113
1114
insertRecompileCheck();
1115
1116
// Insert an interrupt check when recording or replaying, which will bump
1117
// the record/replay system's progress counter.
1118
if (script()->trackRecordReplayProgress()) {
1119
MInterruptCheck* check = MInterruptCheck::New(alloc());
1120
check->setTrackRecordReplayProgress();
1121
current->add(check);
1122
}
1123
1124
// Initialize the env chain now that all resume points operands are
1125
// initialized.
1126
MOZ_TRY(initEnvironmentChain(callInfo.fun()));
1127
1128
auto clearLastPriorResumePoint = mozilla::MakeScopeExit([&] {
1129
// Discard unreferenced & pre-allocated resume points.
1130
replaceMaybeFallbackFunctionGetter(nullptr);
1131
});
1132
1133
MOZ_TRY(traverseBytecode());
1134
1135
MOZ_ASSERT(iterators_.empty(), "Iterators should be added to outer builder");
1136
1137
if (!info().isAnalysis() && !abortedPreliminaryGroups().empty()) {
1138
return abort(AbortReason::PreliminaryObjects);
1139
}
1140
1141
return Ok();
1142
}
1143
1144
void IonBuilder::runTask() {
1145
// This is the entry point when ion compiles are run offthread.
1146
JSRuntime* rt = script()->runtimeFromAnyThread();
1147
1148
TraceLoggerThread* logger = TraceLoggerForCurrentThread();
1149
TraceLoggerEvent event(TraceLogger_AnnotateScripts, script());
1150
AutoTraceLog logScript(logger, event);
1151
AutoTraceLog logCompile(logger, TraceLogger_IonCompilation);
1152
1153
jit::JitContext jctx(jit::CompileRuntime::get(rt),
1154
jit::CompileRealm::get(script()->realm()), &alloc());
1155
setBackgroundCodegen(jit::CompileBackEnd(this));
1156
}
1157
1158
void IonBuilder::rewriteParameter(uint32_t slotIdx, MDefinition* param) {
1159
MOZ_ASSERT(param->isParameter() || param->isGetArgumentsObjectArg());
1160
1161
TemporaryTypeSet* types = param->resultTypeSet();
1162
MDefinition* actual = ensureDefiniteType(param, types->getKnownMIRType());
1163
if (actual == param) {
1164
return;
1165
}
1166
1167
// Careful! We leave the original MParameter in the entry resume point. The
1168
// arguments still need to be checked unless proven otherwise at the call
1169
// site, and these checks can bailout. We can end up:
1170
// v0 = Parameter(0)
1171
// v1 = Unbox(v0, INT32)
1172
// -- ResumePoint(v0)
1173
//
1174
// As usual, it would be invalid for v1 to be captured in the initial
1175
// resume point, rather than v0.
1176
current->rewriteSlot(slotIdx, actual);
1177
}
1178
1179
// Apply Type Inference information to parameters early on, unboxing them if
1180
// they have a definitive type. The actual guards will be emitted by the code
1181
// generator, explicitly, as part of the function prologue.
1182
AbortReasonOr<Ok> IonBuilder::rewriteParameters() {
1183
MOZ_ASSERT(info().environmentChainSlot() == 0);
1184
1185
// If this JSScript is not the code of a function, then skip the
1186
// initialization of function parameters.
1187
if (!info().funMaybeLazy()) {
1188
return Ok();
1189
}
1190
1191
for (uint32_t i = info().startArgSlot(); i < info().endArgSlot(); i++) {
1192
if (!alloc().ensureBallast()) {
1193
return abort(AbortReason::Alloc);
1194
}
1195
MDefinition* param = current->getSlot(i);
1196
rewriteParameter(i, param);
1197
}
1198
1199
return Ok();
1200
}
1201
1202
AbortReasonOr<Ok> IonBuilder::initParameters() {
1203
// If this JSScript is not the code of a function, then skip the
1204
// initialization of function parameters.
1205
if (!info().funMaybeLazy()) {
1206
return Ok();
1207
}
1208
1209
// If we are doing OSR on a frame which initially executed in the
1210
// interpreter and didn't accumulate type information, try to use that OSR
1211
// frame to determine possible initial types for 'this' and parameters.
1212
1213
if (thisTypes->empty() && baselineFrame_) {
1214
TypeSet::Type type = baselineFrame_->thisType;
1215
if (type.isSingletonUnchecked()) {
1216
checkNurseryObject(type.singleton());
1217
}
1218
thisTypes->addType(type, alloc_->lifoAlloc());
1219
}
1220
1221
MParameter* param =
1222
MParameter::New(alloc(), MParameter::THIS_SLOT, thisTypes);
1223
current->add(param);
1224
current->initSlot(info().thisSlot(), param);
1225
1226
for (uint32_t i = 0; i < info().nargs(); i++) {
1227
TemporaryTypeSet* types = &argTypes[i];
1228
if (types->empty() && baselineFrame_ &&
1229
!script_->jitScript()->modifiesArguments()) {
1230
TypeSet::Type type = baselineFrame_->argTypes[i];
1231
if (type.isSingletonUnchecked()) {
1232
checkNurseryObject(type.singleton());
1233
}
1234
types->addType(type, alloc_->lifoAlloc());
1235
}
1236
1237
param = MParameter::New(alloc().fallible(), i, types);
1238
if (!param) {
1239
return abort(AbortReason::Alloc);
1240
}
1241
current->add(param);
1242
current->initSlot(info().argSlotUnchecked(i), param);
1243
}
1244
1245
return Ok();
1246
}
1247
1248
void IonBuilder::initLocals() {
1249
// Initialize all frame slots to undefined. Lexical bindings are temporal
1250
// dead zoned in bytecode.
1251
1252
if (info().nlocals() == 0) {
1253
return;
1254
}
1255
1256
MConstant* undef = MConstant::New(alloc(), UndefinedValue());
1257
current->add(undef);
1258
1259
for (uint32_t i = 0; i < info().nlocals(); i++) {
1260
current->initSlot(info().localSlot(i), undef);
1261
}
1262
}
1263
1264
bool IonBuilder::usesEnvironmentChain() {
1265
return script()->jitScript()->usesEnvironmentChain();
1266
}
1267
1268
AbortReasonOr<Ok> IonBuilder::initEnvironmentChain(MDefinition* callee) {
1269
MInstruction* env = nullptr;
1270
1271
// If the script doesn't use the envchain, then it's already initialized
1272
// from earlier. However, always make a env chain when |needsArgsObj| is true
1273
// for the script, since arguments object construction requires the env chain
1274
// to be passed in.
1275
if (!info().needsArgsObj() && !usesEnvironmentChain()) {
1276
return Ok();
1277
}
1278
1279
// The env chain is only tracked in scripts that have NAME opcodes which
1280
// will try to access the env. For other scripts, the env instructions
1281
// will be held live by resume points and code will still be generated for
1282
// them, so just use a constant undefined value.
1283
1284
if (JSFunction* fun = info().funMaybeLazy()) {
1285
if (!callee) {
1286
MCallee* calleeIns = MCallee::New(alloc());
1287
current->add(calleeIns);
1288
callee = calleeIns;
1289
}
1290
env = MFunctionEnvironment::New(alloc(), callee);
1291
current->add(env);
1292
1293
// This reproduce what is done in CallObject::createForFunction. Skip
1294
// this for the arguments analysis, as the script might not have a
1295
// baseline script with template objects yet.
1296
if (fun->needsSomeEnvironmentObject() &&
1297
info().analysisMode() != Analysis_ArgumentsUsage) {
1298
if (fun->needsNamedLambdaEnvironment()) {
1299
env = createNamedLambdaObject(callee, env);
1300
}
1301
1302
// TODO: Parameter expression-induced extra var environment not
1303
// yet handled.
1304
if (fun->needsExtraBodyVarEnvironment()) {
1305
return abort(AbortReason::Disable, "Extra var environment unsupported");
1306
}
1307
1308
if (fun->needsCallObject()) {
1309
MOZ_TRY_VAR(env, createCallObject(callee, env));
1310
}
1311
}
1312
} else if (ModuleObject* module = info().module()) {
1313
// Modules use a pre-created env object.
1314
env = constant(ObjectValue(module->initialEnvironment()));
1315
} else {
1316
// For global scripts without a non-syntactic global scope, the env
1317
// chain is the global lexical env.
1318
MOZ_ASSERT(!script()->isForEval());
1319
MOZ_ASSERT(!script()->hasNonSyntacticScope());
1320
env = constant(ObjectValue(script()->global().lexicalEnvironment()));
1321
}
1322
1323
// Update the environment slot from UndefinedValue only after initial
1324
// environment is created so that bailout doesn't see a partial env.
1325
// See: |InitFromBailout|
1326
current->setEnvironmentChain(env);
1327
return Ok();
1328
}
1329
1330
void IonBuilder::initArgumentsObject() {
1331
JitSpew(JitSpew_IonMIR,
1332
"%s:%u:%u - Emitting code to initialize arguments object! block=%p",
1333
script()->filename(), script()->lineno(), script()->column(),
1334
current);
1335
MOZ_ASSERT(info().needsArgsObj());
1336
1337
bool mapped = script()->hasMappedArgsObj();
1338
ArgumentsObject* templateObj =
1339
script()->realm()->maybeArgumentsTemplateObject(mapped);
1340
1341
MCreateArgumentsObject* argsObj = MCreateArgumentsObject::New(
1342
alloc(), current->environmentChain(), templateObj);
1343
current->add(argsObj);
1344
current->setArgumentsObject(argsObj);
1345
}
1346
1347
AbortReasonOr<Ok> IonBuilder::addOsrValueTypeBarrier(
1348
uint32_t slot, MInstruction** def_, MIRType type,
1349
TemporaryTypeSet* typeSet) {
1350
MInstruction*& def = *def_;
1351
MBasicBlock* osrBlock = def->block();
1352
1353
// Clear bogus type information added in newOsrPreheader().
1354
def->setResultType(MIRType::Value);
1355
def->setResultTypeSet(nullptr);
1356
1357
if (typeSet && !typeSet->unknown()) {
1358
MInstruction* barrier = MTypeBarrier::New(alloc(), def, typeSet);
1359
osrBlock->insertBefore(osrBlock->lastIns(), barrier);
1360
osrBlock->rewriteSlot(slot, barrier);
1361
def = barrier;
1362
1363
// If the TypeSet is more precise than |type|, adjust |type| for the
1364
// code below.
1365
if (type == MIRType::Value) {
1366
type = barrier->type();
1367
}
1368
} else if (type == MIRType::Null || type == MIRType::Undefined ||
1369
type == MIRType::MagicOptimizedArguments) {
1370
// No unbox instruction will be added below, so check the type by
1371
// adding a type barrier for a singleton type set.
1372
TypeSet::Type ntype = TypeSet::PrimitiveType(ValueTypeFromMIRType(type));
1373
LifoAlloc* lifoAlloc = alloc().lifoAlloc();
1374
typeSet = lifoAlloc->new_<TemporaryTypeSet>(lifoAlloc, ntype);
1375
if (!typeSet) {
1376
return abort(AbortReason::Alloc);
1377
}
1378
MInstruction* barrier = MTypeBarrier::New(alloc(), def, typeSet);
1379
osrBlock->insertBefore(osrBlock->lastIns(), barrier);
1380
osrBlock->rewriteSlot(slot, barrier);
1381
def = barrier;
1382
}
1383
1384
// The following guards aren't directly linked into the usedef chain,
1385
// however in the OSR block we need to ensure they're not optimized out, so we
1386
// mark them as implicitly used.
1387
switch (type) {
1388
case MIRType::Null:
1389
case MIRType::Undefined:
1390
case MIRType::MagicOptimizedArguments:
1391
def->setImplicitlyUsed();
1392
break;
1393
default:
1394
break;
1395
}
1396
1397
switch (type) {
1398
case MIRType::Boolean:
1399
case MIRType::Int32:
1400
case MIRType::Double:
1401
case MIRType::String:
1402
case MIRType::Symbol:
1403
case MIRType::BigInt:
1404
case MIRType::Object:
1405
if (type != def->type()) {
1406
MUnbox* unbox = MUnbox::New(alloc(), def, type, MUnbox::Fallible);
1407
osrBlock->insertBefore(osrBlock->lastIns(), unbox);
1408
osrBlock->rewriteSlot(slot, unbox);
1409
def = unbox;
1410
}
1411
break;
1412
1413
case MIRType::Null: {
1414
MConstant* c = MConstant::New(alloc(), NullValue());
1415
osrBlock->insertBefore(osrBlock->lastIns(), c);
1416
osrBlock->rewriteSlot(slot, c);
1417
def = c;
1418
break;
1419
}
1420
1421
case MIRType::Undefined: {
1422
MConstant* c = MConstant::New(alloc(), UndefinedValue());
1423
osrBlock->insertBefore(osrBlock->lastIns(), c);
1424
osrBlock->rewriteSlot(slot, c);
1425
def = c;
1426
break;
1427
}
1428
1429
case MIRType::MagicOptimizedArguments: {
1430
MOZ_ASSERT(hasLazyArguments_);
1431
MConstant* lazyArg =
1432
MConstant::New(alloc(), MagicValue(JS_OPTIMIZED_ARGUMENTS));
1433
osrBlock->insertBefore(osrBlock->lastIns(), lazyArg);
1434
osrBlock->rewriteSlot(slot, lazyArg);
1435
def = lazyArg;
1436
break;
1437
}
1438
1439
default:
1440
break;
1441
}
1442
1443
MOZ_ASSERT(def == osrBlock->getSlot(slot));
1444
return Ok();
1445
}
1446
1447
AbortReasonOr<Ok> IonBuilder::maybeAddOsrTypeBarriers() {
1448
if (!info().osrPc()) {
1449
return Ok();
1450
}
1451
1452
// The loop has successfully been processed, and the loop header phis
1453
// have their final type. Add unboxes and type barriers in the OSR
1454
// block to check that the values have the appropriate type, and update
1455
// the types in the preheader.
1456
1457
MBasicBlock* osrBlock = graph().osrBlock();
1458
if (!osrBlock) {
1459
// Because IonBuilder does not compile catch blocks, it's possible to
1460
// end up without an OSR block if the OSR pc is only reachable via a
1461
// break-statement inside the catch block. For instance:
1462
//
1463
// for (;;) {
1464
// try {
1465
// throw 3;
1466
// } catch(e) {
1467
// break;
1468
// }
1469
// }
1470
// while (..) { } // <= OSR here, only reachable via catch block.
1471
//
1472
// For now we just abort in this case.
1473
MOZ_ASSERT(graph().hasTryBlock());
1474
return abort(AbortReason::Disable,
1475
"OSR block only reachable through catch block");
1476
}
1477
1478
MBasicBlock* preheader = osrBlock->getSuccessor(0);
1479
MBasicBlock* header = preheader->getSuccessor(0);
1480
static const size_t OSR_PHI_POSITION = 1;
1481
MOZ_ASSERT(preheader->getPredecessor(OSR_PHI_POSITION) == osrBlock);
1482
1483
MResumePoint* headerRp = header->entryResumePoint();
1484
size_t stackDepth = headerRp->stackDepth();
1485
MOZ_ASSERT(stackDepth == osrBlock->stackDepth());
1486
for (uint32_t slot = info().startArgSlot(); slot < stackDepth; slot++) {
1487
// Aliased slots are never accessed, since they need to go through
1488
// the callobject. The typebarriers are added there and can be
1489
// discarded here.
1490
if (info().isSlotAliased(slot)) {
1491
continue;
1492
}
1493
1494
if (!alloc().ensureBallast()) {
1495
return abort(AbortReason::Alloc);
1496
}
1497
1498
MInstruction* def = osrBlock->getSlot(slot)->toInstruction();
1499
MPhi* preheaderPhi = preheader->getSlot(slot)->toPhi();
1500
MPhi* headerPhi = headerRp->getOperand(slot)->toPhi();
1501
1502
MIRType type = headerPhi->type();
1503
TemporaryTypeSet* typeSet = headerPhi->resultTypeSet();
1504
1505
MOZ_TRY(addOsrValueTypeBarrier(slot, &def, type, typeSet));
1506
1507
preheaderPhi->replaceOperand(OSR_PHI_POSITION, def);
1508
preheaderPhi->setResultType(type);
1509
preheaderPhi->setResultTypeSet(typeSet);
1510
}
1511
1512
return Ok();
1513
}
1514
1515
enum class CFGState : uint32_t { Alloc = 0, Abort = 1, Success = 2 };
1516
1517
static CFGState GetOrCreateControlFlowGraph(TempAllocator& tempAlloc,
1518
JSScript* script,
1519
const ControlFlowGraph** cfgOut) {
1520
if (script->jitScript()->controlFlowGraph()) {
1521
*cfgOut = script->jitScript()->controlFlowGraph();
1522
return CFGState::Success;
1523
}
1524
1525
ControlFlowGenerator cfgenerator(tempAlloc, script);
1526
if (!cfgenerator.traverseBytecode()) {
1527
if (cfgenerator.aborted()) {
1528
return CFGState::Abort;
1529
}
1530
return CFGState::Alloc;
1531
}
1532
1533
// Cache the control flow graph on the JitScript.
1534
LifoAlloc& lifoAlloc = script->zone()->jitZone()->cfgSpace()->lifoAlloc();
1535
LifoAlloc::AutoFallibleScope fallibleAllocator(&lifoAlloc);
1536
TempAllocator* graphAlloc = lifoAlloc.new_<TempAllocator>(&lifoAlloc);
1537
if (!graphAlloc) {
1538
return CFGState::Alloc;
1539
}
1540
1541
ControlFlowGraph* cfg = cfgenerator.getGraph(*graphAlloc);
1542
if (!cfg) {
1543
return CFGState::Alloc;
1544
}
1545
1546
MOZ_ASSERT(!script->jitScript()->controlFlowGraph());
1547
script->jitScript()->setControlFlowGraph(cfg);
1548
1549
if (JitSpewEnabled(JitSpew_CFG)) {
1550
JitSpew(JitSpew_CFG, "Generating graph for %s:%u:%u", script->filename(),
1551
script->lineno(), script->column());
1552
Fprinter& print = JitSpewPrinter();
1553
cfg->dump(print, script);
1554
}
1555
1556
*cfgOut = cfg;
1557
return CFGState::Success;
1558
}
1559
1560
// We traverse the bytecode using the control flow graph. This structure
1561
// contains a graph of CFGBlocks in RPO order.
1562
//
1563
// Per CFGBlock we take the corresponding MBasicBlock and start iterating the
1564
// bytecode of that CFGBlock. Each basic block has a mapping of local slots to
1565
// instructions, as well as a stack depth. As we encounter instructions we
1566
// mutate this mapping in the current block.
1567
//
1568
// Afterwards we visit the control flow instruction. There we add the ending ins
1569
// of the MBasicBlock and create new MBasicBlocks for the successors. That means
1570
// adding phi nodes for diamond join points, making sure to propagate types
1571
// around loops ...
1572
//
1573
// We keep a link between a CFGBlock and the entry MBasicBlock (in
1574
// blockWorklist). That vector only contains the MBasicBlocks that correspond
1575
// with a CFGBlock. We can create new MBasicBlocks that don't correspond to a
1576
// CFGBlock.
1577
AbortReasonOr<Ok> IonBuilder::traverseBytecode() {
1578
CFGState state = GetOrCreateControlFlowGraph(alloc(), info().script(), &cfg);
1579
MOZ_ASSERT_IF(cfg, info().script()->jitScript()->controlFlowGraph() == cfg);
1580
if (state == CFGState::Alloc) {
1581
return abort(AbortReason::Alloc);
1582
}
1583
if (state == CFGState::Abort) {
1584
return abort(AbortReason::Disable, "Couldn't create the CFG of script");
1585
}
1586
1587
if (!blockWorklist.growBy(cfg->numBlocks())) {
1588
return abort(AbortReason::Alloc);
1589
}
1590
blockWorklist[0] = current;
1591
1592
size_t i = 0;
1593
while (i < cfg->numBlocks()) {
1594
if (!alloc().ensureBallast()) {
1595
return abort(AbortReason::Alloc);
1596
}
1597
1598
bool restarted = false;
1599
const CFGBlock* cfgblock = cfg->block(i);
1600
MBasicBlock* mblock = blockWorklist[i];
1601
MOZ_ASSERT(mblock && !mblock->isDead());
1602
1603
MOZ_TRY(visitBlock(cfgblock, mblock));
1604
MOZ_TRY(visitControlInstruction(cfgblock->stopIns(), &restarted));
1605
1606
if (restarted) {
1607
// Move back to the start of the loop.
1608
while (!blockWorklist[i] || blockWorklist[i]->isDead()) {
1609
MOZ_ASSERT(i > 0);
1610
i--;
1611
}
1612
MOZ_ASSERT(cfgblock->stopIns()->isBackEdge());
1613
MOZ_ASSERT(loopHeaderStack_.back() == blockWorklist[i]);
1614
} else {
1615
i++;
1616
}
1617
}
1618
1619
#ifdef DEBUG
1620
MOZ_ASSERT(graph().numBlocks() >= blockWorklist.length());
1621
for (i = 0; i < cfg->numBlocks(); i++) {
1622
MOZ_ASSERT(blockWorklist[i]);
1623
MOZ_ASSERT(!blockWorklist[i]->isDead());
1624
MOZ_ASSERT_IF(i != 0, blockWorklist[i]->id() != 0);
1625
}
1626
#endif
1627
1628
cfg = nullptr;
1629
1630
blockWorklist.clear();
1631
return Ok();
1632
}
1633
1634
AbortReasonOr<Ok> IonBuilder::visitBlock(const CFGBlock* cfgblock,
1635
MBasicBlock* mblock) {
1636
mblock->setLoopDepth(loopDepth_);
1637
1638
cfgCurrent = cfgblock;
1639
pc = cfgblock->startPc();
1640
1641
if (mblock->pc() && script()->hasScriptCounts()) {
1642
mblock->setHitCount(script()->getHitCount(mblock->pc()));
1643
}
1644
1645
// Optimization to move a predecessor that only has this block as successor
1646
// just before this block. Skip this optimization if the previous block is
1647
// not part of the same function, as we might have to backtrack on inlining
1648
// failures.
1649
if (mblock->numPredecessors() == 1 &&
1650
mblock->getPredecessor(0)->numSuccessors() == 1 &&
1651
!mblock->getPredecessor(0)->outerResumePoint()) {
1652
graph().removeBlockFromList(mblock->getPredecessor(0));
1653
graph().addBlock(mblock->getPredecessor(0));
1654
}
1655
1656
MOZ_TRY(setCurrentAndSpecializePhis(mblock));
1657
graph().addBlock(mblock);
1658
1659
while (pc < cfgblock->stopPc()) {
1660
if (!alloc().ensureBallast()) {
1661
return abort(AbortReason::Alloc);
1662
}
1663
1664
#ifdef DEBUG
1665
// In debug builds, after compiling this op, check that all values
1666
// popped by this opcode either:
1667
//
1668
// (1) Have the ImplicitlyUsed flag set on them.
1669
// (2) Have more uses than before compiling this op (the value is
1670
// used as operand of a new MIR instruction).
1671
//
1672
// This is used to catch problems where IonBuilder pops a value without
1673
// adding any SSA uses and doesn't call setImplicitlyUsedUnchecked on it.
1674
Vector<MDefinition*, 4, JitAllocPolicy> popped(alloc());
1675
Vector<size_t, 4, JitAllocPolicy> poppedUses(alloc());
1676
unsigned nuses = GetUseCount(pc);
1677
1678
for (unsigned i = 0; i < nuses; i++) {
1679
MDefinition* def = current->peek(-int32_t(i + 1));
1680
if (!popped.append(def) || !poppedUses.append(def->defUseCount())) {
1681
return abort(AbortReason::Alloc);
1682
}
1683
}
1684
#endif
1685
1686
// Nothing in inspectOpcode() is allowed to advance the pc.
1687
JSOp op = JSOp(*pc);
1688
MOZ_TRY(inspectOpcode(op));
1689
1690
#ifdef DEBUG
1691
for (size_t i = 0; i < popped.length(); i++) {
1692
switch (op) {
1693
case JSOP_POP:
1694
case JSOP_POPN:
1695
case JSOP_DUPAT:
1696
case JSOP_DUP:
1697
case JSOP_DUP2:
1698
case JSOP_PICK:
1699
case JSOP_UNPICK:
1700
case JSOP_SWAP:
1701
case JSOP_SETARG:
1702
case JSOP_SETLOCAL:
1703
case JSOP_INITLEXICAL:
1704
case JSOP_SETRVAL:
1705
case JSOP_VOID:
1706
// Don't require SSA uses for values popped by these ops.
1707
break;
1708
1709
case JSOP_POS:
1710
case JSOP_TONUMERIC:
1711
case JSOP_TOID:
1712
case JSOP_TOSTRING:
1713
// These ops may leave their input on the stack without setting
1714
// the ImplicitlyUsed flag. If this value will be popped immediately,
1715
// we may replace it with |undefined|, but the difference is
1716
// not observable.
1717
MOZ_ASSERT(i == 0);
1718
if (current->peek(-1) == popped[0]) {
1719
break;
1720
}
1721
MOZ_FALLTHROUGH;
1722
1723
default:
1724
MOZ_ASSERT(popped[i]->isImplicitlyUsed() ||
1725
1726
// MNewDerivedTypedObject instances are
1727
// often dead unless they escape from the
1728
// fn. See IonBuilder::loadTypedObjectData()
1729
// for more details.
1730
popped[i]->isNewDerivedTypedObject() ||
1731
1732
popped[i]->defUseCount() > poppedUses[i]);
1733
break;
1734
}
1735
}
1736
#endif
1737
1738
pc += CodeSpec[op].length;
1739
current->updateTrackedSite(bytecodeSite(pc));
1740
}
1741
return Ok();
1742
}
1743