Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/js/src/jit/LIR.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2
 * vim: set ts=8 sts=4 et sw=4 tw=99:
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/LIR.h"
8
9
#include "mozilla/ScopeExit.h"
10
11
#include <ctype.h>
12
#include <type_traits>
13
14
#include "jit/JitSpewer.h"
15
#include "jit/MIR.h"
16
#include "jit/MIRGenerator.h"
17
#include "js/Printf.h"
18
19
using namespace js;
20
using namespace js::jit;
21
22
LIRGraph::LIRGraph(MIRGraph* mir)
23
  : blocks_(),
24
    constantPool_(mir->alloc()),
25
    constantPoolMap_(mir->alloc()),
26
    safepoints_(mir->alloc()),
27
    nonCallSafepoints_(mir->alloc()),
28
    numVirtualRegisters_(0),
29
    numInstructions_(1), // First id is 1.
30
    localSlotCount_(0),
31
    argumentSlotCount_(0),
32
    entrySnapshot_(nullptr),
33
    mir_(*mir)
34
14
{
35
14
}
36
37
bool
38
LIRGraph::addConstantToPool(const Value& v, uint32_t* index)
39
154
{
40
154
    ConstantPoolMap::AddPtr p = constantPoolMap_.lookupForAdd(v);
41
154
    if (p) {
42
140
        *index = p->value();
43
140
        return true;
44
140
    }
45
14
    *index = constantPool_.length();
46
14
    return constantPool_.append(v) && constantPoolMap_.add(p, v, *index);
47
14
}
48
49
bool
50
LIRGraph::noteNeedsSafepoint(LInstruction* ins)
51
149
{
52
149
    // Instructions with safepoints must be in linear order.
53
149
    MOZ_ASSERT_IF(!safepoints_.empty(), safepoints_.back()->id() < ins->id());
54
149
    if (!ins->isCall() && !nonCallSafepoints_.append(ins)) {
55
0
        return false;
56
0
    }
57
149
    return safepoints_.append(ins);
58
149
}
59
60
#ifdef JS_JITSPEW
61
void
62
LIRGraph::dump(GenericPrinter& out)
63
{
64
    for (size_t i = 0; i < numBlocks(); i++) {
65
        getBlock(i)->dump(out);
66
        out.printf("\n");
67
    }
68
}
69
70
void
71
LIRGraph::dump()
72
{
73
    Fprinter out(stderr);
74
    dump(out);
75
    out.finish();
76
}
77
#endif
78
79
LBlock::LBlock(MBasicBlock* from)
80
  : block_(from),
81
    phis_(),
82
    entryMoveGroup_(nullptr),
83
    exitMoveGroup_(nullptr)
84
56
{
85
56
    from->assignLir(this);
86
56
}
87
88
bool
89
LBlock::init(TempAllocator& alloc)
90
56
{
91
56
    // Count the number of LPhis we'll need.
92
56
    size_t numLPhis = 0;
93
56
    for (MPhiIterator i(block_->phisBegin()), e(block_->phisEnd()); i != e; ++i) {
94
0
        MPhi* phi = *i;
95
0
        switch (phi->type()) {
96
0
          case MIRType::Value: numLPhis += BOX_PIECES; break;
97
0
          case MIRType::Int64: numLPhis += INT64_PIECES; break;
98
0
          default: numLPhis += 1; break;
99
0
        }
100
0
    }
101
56
102
56
    // Allocate space for the LPhis.
103
56
    if (!phis_.init(alloc, numLPhis)) {
104
0
        return false;
105
0
    }
106
56
107
56
    // For each MIR phi, set up LIR phis as appropriate. We'll fill in their
108
56
    // operands on each incoming edge, and set their definitions at the start of
109
56
    // their defining block.
110
56
    size_t phiIndex = 0;
111
56
    size_t numPreds = block_->numPredecessors();
112
56
    for (MPhiIterator i(block_->phisBegin()), e(block_->phisEnd()); i != e; ++i) {
113
0
        MPhi* phi = *i;
114
0
        MOZ_ASSERT(phi->numOperands() == numPreds);
115
0
116
0
        int numPhis;
117
0
        switch (phi->type()) {
118
0
          case MIRType::Value: numPhis = BOX_PIECES; break;
119
0
          case MIRType::Int64: numPhis = INT64_PIECES; break;
120
0
          default: numPhis = 1; break;
121
0
        }
122
0
        for (int i = 0; i < numPhis; i++) {
123
0
            LAllocation* inputs = alloc.allocateArray<LAllocation>(numPreds);
124
0
            if (!inputs) {
125
0
                return false;
126
0
            }
127
0
128
0
            void* addr = &phis_[phiIndex++];
129
0
            LPhi* lphi = new (addr) LPhi(phi, inputs);
130
0
            lphi->setBlock(this);
131
0
        }
132
0
    }
133
56
    return true;
134
56
}
135
136
const LInstruction*
137
LBlock::firstInstructionWithId() const
138
56
{
139
56
    for (LInstructionIterator i(instructions_.begin()); i != instructions_.end(); ++i) {
140
56
        if (i->id()) {
141
56
            return *i;
142
56
        }
143
56
    }
144
56
    return 0;
145
56
}
146
147
LMoveGroup*
148
LBlock::getEntryMoveGroup(TempAllocator& alloc)
149
14
{
150
14
    if (entryMoveGroup_) {
151
0
        return entryMoveGroup_;
152
0
    }
153
14
    entryMoveGroup_ = LMoveGroup::New(alloc);
154
14
    insertBefore(*begin(), entryMoveGroup_);
155
14
    return entryMoveGroup_;
156
14
}
157
158
LMoveGroup*
159
LBlock::getExitMoveGroup(TempAllocator& alloc)
160
0
{
161
0
    if (exitMoveGroup_) {
162
0
        return exitMoveGroup_;
163
0
    }
164
0
    exitMoveGroup_ = LMoveGroup::New(alloc);
165
0
    insertBefore(*rbegin(), exitMoveGroup_);
166
0
    return exitMoveGroup_;
167
0
}
168
169
#ifdef JS_JITSPEW
170
void
171
LBlock::dump(GenericPrinter& out)
172
{
173
    out.printf("block%u:\n", mir()->id());
174
    for (size_t i = 0; i < numPhis(); ++i) {
175
        getPhi(i)->dump(out);
176
        out.printf("\n");
177
    }
178
    for (LInstructionIterator iter = begin(); iter != end(); iter++) {
179
        iter->dump(out);
180
        out.printf("\n");
181
    }
182
}
183
184
void
185
LBlock::dump()
186
{
187
    Fprinter out(stderr);
188
    dump(out);
189
    out.finish();
190
}
191
#endif
192
193
static size_t
194
TotalOperandCount(LRecoverInfo* recoverInfo)
195
350
{
196
350
    size_t accum = 0;
197
3.40k
    for (LRecoverInfo::OperandIter it(recoverInfo); !it; ++it) {
198
3.05k
        if (!it->isRecoveredOnBailout()) {
199
3.05k
            accum++;
200
3.05k
        }
201
3.05k
    }
202
350
    return accum;
203
350
}
204
205
LRecoverInfo::LRecoverInfo(TempAllocator& alloc)
206
  : instructions_(alloc),
207
    recoverOffset_(INVALID_RECOVER_OFFSET)
208
175
{ }
209
210
LRecoverInfo*
211
LRecoverInfo::New(MIRGenerator* gen, MResumePoint* mir)
212
175
{
213
175
    LRecoverInfo* recoverInfo = new(gen->alloc()) LRecoverInfo(gen->alloc());
214
175
    if (!recoverInfo || !recoverInfo->init(mir)) {
215
0
        return nullptr;
216
0
    }
217
175
218
175
    JitSpew(JitSpew_IonSnapshots, "Generating LIR recover info %p from MIR (%p)",
219
175
            (void*)recoverInfo, (void*)mir);
220
175
221
175
    return recoverInfo;
222
175
}
223
224
// de-virtualise MResumePoint::getOperand calls.
225
template <typename Node>
226
bool
227
LRecoverInfo::appendOperands(Node* ins)
228
175
{
229
1.65k
    for (size_t i = 0, end = ins->numOperands(); i < end; i++) {
230
1.48k
        MDefinition* def = ins->getOperand(i);
231
1.48k
232
1.48k
        // As there is no cycle in the data-flow (without MPhi), checking for
233
1.48k
        // isInWorkList implies that the definition is already in the
234
1.48k
        // instruction vector, and not processed by a caller of the current
235
1.48k
        // function.
236
1.48k
        if (def->isRecoveredOnBailout() && !def->isInWorklist()) {
237
0
            if (!appendDefinition(def)) {
238
0
                return false;
239
0
            }
240
0
        }
241
1.48k
    }
242
175
243
175
    return true;
244
175
}
Unexecuted instantiation: bool js::jit::LRecoverInfo::appendOperands<js::jit::MDefinition>(js::jit::MDefinition*)
bool js::jit::LRecoverInfo::appendOperands<js::jit::MResumePoint>(js::jit::MResumePoint*)
Line
Count
Source
228
175
{
229
1.65k
    for (size_t i = 0, end = ins->numOperands(); i < end; i++) {
230
1.48k
        MDefinition* def = ins->getOperand(i);
231
1.48k
232
1.48k
        // As there is no cycle in the data-flow (without MPhi), checking for
233
1.48k
        // isInWorkList implies that the definition is already in the
234
1.48k
        // instruction vector, and not processed by a caller of the current
235
1.48k
        // function.
236
1.48k
        if (def->isRecoveredOnBailout() && !def->isInWorklist()) {
237
0
            if (!appendDefinition(def)) {
238
0
                return false;
239
0
            }
240
0
        }
241
1.48k
    }
242
175
243
175
    return true;
244
175
}
245
246
bool
247
LRecoverInfo::appendDefinition(MDefinition* def)
248
0
{
249
0
    MOZ_ASSERT(def->isRecoveredOnBailout());
250
0
    def->setInWorklist();
251
0
    auto clearWorklistFlagOnFailure = mozilla::MakeScopeExit([&] {
252
0
        def->setNotInWorklist();
253
0
    });
254
0
255
0
    if (!appendOperands(def)) {
256
0
        return false;
257
0
    }
258
0
259
0
    if (!instructions_.append(def)) {
260
0
        return false;
261
0
    }
262
0
263
0
    clearWorklistFlagOnFailure.release();
264
0
    return true;
265
0
}
266
267
bool
268
LRecoverInfo::appendResumePoint(MResumePoint* rp)
269
175
{
270
175
    // Stores should be recovered first.
271
175
    for (auto iter(rp->storesBegin()), end(rp->storesEnd()); iter != end; ++iter) {
272
0
        if (!appendDefinition(iter->operand)) {
273
0
            return false;
274
0
        }
275
0
    }
276
175
277
175
    if (rp->caller() && !appendResumePoint(rp->caller())) {
278
0
        return false;
279
0
    }
280
175
281
175
    if (!appendOperands(rp)) {
282
0
        return false;
283
0
    }
284
175
285
175
    return instructions_.append(rp);
286
175
}
287
288
bool
289
LRecoverInfo::init(MResumePoint* rp)
290
175
{
291
175
    // Before exiting this function, remove temporary flags from all definitions
292
175
    // added in the vector.
293
175
    auto clearWorklistFlags = mozilla::MakeScopeExit([&] {
294
350
        for (MNode** it = begin(); it != end(); it++) {
295
175
            if (!(*it)->isDefinition()) {
296
175
                continue;
297
175
            }
298
0
            (*it)->toDefinition()->setNotInWorklist();
299
0
        }
300
175
    });
301
175
302
175
    // Sort operations in the order in which we need to restore the stack. This
303
175
    // implies that outer frames, as well as operations needed to recover the
304
175
    // current frame, are located before the current frame. The inner-most
305
175
    // resume point should be the last element in the list.
306
175
    if (!appendResumePoint(rp)) {
307
0
        return false;
308
0
    }
309
175
310
175
    MOZ_ASSERT(mir() == rp);
311
175
    return true;
312
175
}
313
314
LSnapshot::LSnapshot(LRecoverInfo* recoverInfo, BailoutKind kind)
315
  : numSlots_(TotalOperandCount(recoverInfo) * BOX_PIECES),
316
    slots_(nullptr),
317
    recoverInfo_(recoverInfo),
318
    snapshotOffset_(INVALID_SNAPSHOT_OFFSET),
319
    bailoutId_(INVALID_BAILOUT_ID),
320
    bailoutKind_(kind)
321
350
{ }
322
323
bool
324
LSnapshot::init(MIRGenerator* gen)
325
350
{
326
350
    slots_ = gen->allocate<LAllocation>(numSlots_);
327
350
    return !!slots_;
328
350
}
329
330
LSnapshot*
331
LSnapshot::New(MIRGenerator* gen, LRecoverInfo* recover, BailoutKind kind)
332
350
{
333
350
    LSnapshot* snapshot = new(gen->alloc()) LSnapshot(recover, kind);
334
350
    if (!snapshot || !snapshot->init(gen)) {
335
0
        return nullptr;
336
0
    }
337
350
338
350
    JitSpew(JitSpew_IonSnapshots, "Generating LIR snapshot %p from recover (%p)",
339
350
            (void*)snapshot, (void*)recover);
340
350
341
350
    return snapshot;
342
350
}
343
344
void
345
LSnapshot::rewriteRecoveredInput(LUse input)
346
0
{
347
0
    // Mark any operands to this snapshot with the same value as input as being
348
0
    // equal to the instruction's result.
349
0
    for (size_t i = 0; i < numEntries(); i++) {
350
0
        if (getEntry(i)->isUse() && getEntry(i)->toUse()->virtualRegister() == input.virtualRegister()) {
351
0
            setEntry(i, LUse(input.virtualRegister(), LUse::RECOVERED_INPUT));
352
0
        }
353
0
    }
354
0
}
355
356
#ifdef JS_JITSPEW
357
void
358
LNode::printName(GenericPrinter& out, Opcode op)
359
{
360
    static const char * const names[] =
361
    {
362
# define LIROP(x) #x,
363
        LIR_OPCODE_LIST(LIROP)
364
# undef LIROP
365
    };
366
    const char* name = names[uint32_t(op)];
367
    size_t len = strlen(name);
368
    for (size_t i = 0; i < len; i++) {
369
        out.printf("%c", tolower(name[i]));
370
    }
371
}
372
373
void
374
LNode::printName(GenericPrinter& out)
375
{
376
    printName(out, op());
377
}
378
#endif
379
380
bool
381
LAllocation::aliases(const LAllocation& other) const
382
3.30k
{
383
3.30k
    if (isFloatReg() && other.isFloatReg()) {
384
0
        return toFloatReg()->reg().aliases(other.toFloatReg()->reg());
385
0
    }
386
3.30k
    return *this == other;
387
3.30k
}
388
389
#ifdef JS_JITSPEW
390
static const char*
391
DefTypeName(LDefinition::Type type)
392
{
393
    switch (type) {
394
      case LDefinition::GENERAL: return "g";
395
      case LDefinition::INT32: return "i";
396
      case LDefinition::OBJECT: return "o";
397
      case LDefinition::SLOTS: return "s";
398
      case LDefinition::FLOAT32: return "f";
399
      case LDefinition::DOUBLE: return "d";
400
      case LDefinition::SIMD128INT: return "simd128int";
401
      case LDefinition::SIMD128FLOAT: return "simd128float";
402
      case LDefinition::SINCOS: return "sincos";
403
#ifdef JS_NUNBOX32
404
      case LDefinition::TYPE: return "t";
405
      case LDefinition::PAYLOAD: return "p";
406
#else
407
      case LDefinition::BOX: return "x";
408
#endif
409
    }
410
    MOZ_CRASH("Invalid type");
411
}
412
413
UniqueChars
414
LDefinition::toString() const
415
{
416
    AutoEnterOOMUnsafeRegion oomUnsafe;
417
418
    UniqueChars buf;
419
    if (isBogusTemp()) {
420
        buf = JS_smprintf("bogus");
421
    } else {
422
        buf = JS_smprintf("v%u<%s>", virtualRegister(), DefTypeName(type()));
423
        if (buf) {
424
            if (policy() == LDefinition::FIXED) {
425
                buf = JS_sprintf_append(std::move(buf), ":%s", output()->toString().get());
426
            } else if (policy() == LDefinition::MUST_REUSE_INPUT) {
427
                buf = JS_sprintf_append(std::move(buf), ":tied(%u)", getReusedInput());
428
            }
429
        }
430
    }
431
432
    if (!buf) {
433
        oomUnsafe.crash("LDefinition::toString()");
434
    }
435
436
    return buf;
437
}
438
439
static UniqueChars
440
PrintUse(const LUse* use)
441
{
442
    switch (use->policy()) {
443
      case LUse::REGISTER:
444
        return JS_smprintf("v%d:r", use->virtualRegister());
445
      case LUse::FIXED:
446
        return JS_smprintf("v%d:%s", use->virtualRegister(),
447
                           AnyRegister::FromCode(use->registerCode()).name());
448
      case LUse::ANY:
449
        return JS_smprintf("v%d:r?", use->virtualRegister());
450
      case LUse::KEEPALIVE:
451
        return JS_smprintf("v%d:*", use->virtualRegister());
452
      case LUse::RECOVERED_INPUT:
453
        return JS_smprintf("v%d:**", use->virtualRegister());
454
      default:
455
        MOZ_CRASH("invalid use policy");
456
    }
457
}
458
459
UniqueChars
460
LAllocation::toString() const
461
{
462
    AutoEnterOOMUnsafeRegion oomUnsafe;
463
464
    UniqueChars buf;
465
    if (isBogus()) {
466
        buf = JS_smprintf("bogus");
467
    } else {
468
        switch (kind()) {
469
          case LAllocation::CONSTANT_VALUE:
470
          case LAllocation::CONSTANT_INDEX:
471
            buf = JS_smprintf("c");
472
            break;
473
          case LAllocation::GPR:
474
            buf = JS_smprintf("%s", toGeneralReg()->reg().name());
475
            break;
476
          case LAllocation::FPU:
477
            buf = JS_smprintf("%s", toFloatReg()->reg().name());
478
            break;
479
          case LAllocation::STACK_SLOT:
480
            buf = JS_smprintf("stack:%d", toStackSlot()->slot());
481
            break;
482
          case LAllocation::ARGUMENT_SLOT:
483
            buf = JS_smprintf("arg:%d", toArgument()->index());
484
            break;
485
          case LAllocation::USE:
486
            buf = PrintUse(toUse());
487
            break;
488
          default:
489
            MOZ_CRASH("what?");
490
        }
491
    }
492
493
    if (!buf) {
494
        oomUnsafe.crash("LAllocation::toString()");
495
    }
496
497
    return buf;
498
}
499
500
void
501
LAllocation::dump() const
502
{
503
    fprintf(stderr, "%s\n", toString().get());
504
}
505
506
void
507
LDefinition::dump() const
508
{
509
    fprintf(stderr, "%s\n", toString().get());
510
}
511
512
template <typename T>
513
static void
514
PrintOperands(GenericPrinter& out, T* node)
515
{
516
    size_t numOperands = node->numOperands();
517
518
    for (size_t i = 0; i < numOperands; i++) {
519
        out.printf(" (%s)", node->getOperand(i)->toString().get());
520
        if (i != numOperands - 1) {
521
            out.printf(",");
522
        }
523
    }
524
}
525
526
void
527
LNode::printOperands(GenericPrinter& out)
528
{
529
    if (isMoveGroup()) {
530
        toMoveGroup()->printOperands(out);
531
        return;
532
    }
533
534
    if (isPhi()) {
535
        PrintOperands(out, toPhi());
536
    } else {
537
        PrintOperands(out, toInstruction());
538
    }
539
}
540
#endif
541
542
void
543
LInstruction::assignSnapshot(LSnapshot* snapshot)
544
350
{
545
350
    MOZ_ASSERT(!snapshot_);
546
350
    snapshot_ = snapshot;
547
350
548
#ifdef JS_JITSPEW
549
    if (JitSpewEnabled(JitSpew_IonSnapshots)) {
550
        JitSpewHeader(JitSpew_IonSnapshots);
551
        Fprinter& out = JitSpewPrinter();
552
        out.printf("Assigning snapshot %p to instruction %p (",
553
                   (void*)snapshot, (void*)this);
554
        printName(out);
555
        out.printf(")\n");
556
    }
557
#endif
558
}
559
560
#ifdef JS_JITSPEW
561
static size_t
562
NumSuccessorsHelper(const LNode* ins)
563
{
564
    return 0;
565
}
566
567
template <size_t Succs, size_t Operands, size_t Temps>
568
static size_t
569
NumSuccessorsHelper(const LControlInstructionHelper<Succs, Operands, Temps>* ins)
570
{
571
    return Succs;
572
}
573
574
static size_t
575
NumSuccessors(const LInstruction* ins)
576
{
577
    switch (ins->op()) {
578
      default: MOZ_CRASH("Unexpected LIR op");
579
# define LIROP(x) case LNode::Opcode::x: return NumSuccessorsHelper(ins->to##x());
580
    LIR_OPCODE_LIST(LIROP)
581
# undef LIROP
582
    }
583
}
584
585
static MBasicBlock*
586
GetSuccessorHelper(const LNode* ins, size_t i)
587
{
588
    MOZ_CRASH("Unexpected instruction with successors");
589
}
590
591
template <size_t Succs, size_t Operands, size_t Temps>
592
static MBasicBlock*
593
GetSuccessorHelper(const LControlInstructionHelper<Succs, Operands, Temps>* ins, size_t i)
594
{
595
    return ins->getSuccessor(i);
596
}
597
598
static MBasicBlock*
599
GetSuccessor(const LInstruction* ins, size_t i)
600
{
601
    MOZ_ASSERT(i < NumSuccessors(ins));
602
603
    switch (ins->op()) {
604
      default: MOZ_CRASH("Unexpected LIR op");
605
# define LIROP(x) case LNode::Opcode::x: return GetSuccessorHelper(ins->to##x(), i);
606
    LIR_OPCODE_LIST(LIROP)
607
# undef LIROP
608
    }
609
}
610
#endif
611
612
#ifdef JS_JITSPEW
613
void
614
LNode::dump(GenericPrinter& out)
615
{
616
    if (numDefs() != 0) {
617
        out.printf("{");
618
        for (size_t i = 0; i < numDefs(); i++) {
619
            const LDefinition* def = isPhi() ? toPhi()->getDef(i) : toInstruction()->getDef(i);
620
            out.printf("%s", def->toString().get());
621
            if (i != numDefs() - 1) {
622
                out.printf(", ");
623
            }
624
        }
625
        out.printf("} <- ");
626
    }
627
628
    printName(out);
629
    printOperands(out);
630
631
    if (isInstruction()) {
632
        LInstruction* ins = toInstruction();
633
        size_t numTemps = ins->numTemps();
634
        if (numTemps > 0) {
635
            out.printf(" t=(");
636
            for (size_t i = 0; i < numTemps; i++) {
637
                out.printf("%s", ins->getTemp(i)->toString().get());
638
                if (i != numTemps - 1) {
639
                    out.printf(", ");
640
                }
641
            }
642
            out.printf(")");
643
        }
644
645
#ifdef JS_JITSPEW
646
        size_t numSuccessors = NumSuccessors(ins);
647
        if (numSuccessors > 0) {
648
            out.printf(" s=(");
649
            for (size_t i = 0; i < numSuccessors; i++) {
650
                MBasicBlock* succ = GetSuccessor(ins, i);
651
                out.printf("block%u", succ->id());
652
                if (i != numSuccessors - 1) {
653
                    out.printf(", ");
654
                }
655
            }
656
            out.printf(")");
657
        }
658
#endif
659
    }
660
}
661
662
void
663
LNode::dump()
664
{
665
    Fprinter out(stderr);
666
    dump(out);
667
    out.printf("\n");
668
    out.finish();
669
}
670
671
const char*
672
LNode::getExtraName() const
673
{
674
    switch (op()) {
675
      default: MOZ_CRASH("Unexpected LIR op");
676
# define LIROP(x) case LNode::Opcode::x: return to##x()->extraName();
677
    LIR_OPCODE_LIST(LIROP)
678
# undef LIROP
679
    }
680
}
681
#endif
682
683
void
684
LInstruction::initSafepoint(TempAllocator& alloc)
685
149
{
686
149
    MOZ_ASSERT(!safepoint_);
687
149
    safepoint_ = new(alloc) LSafepoint(alloc);
688
149
    MOZ_ASSERT(safepoint_);
689
149
}
690
691
bool
692
LMoveGroup::add(LAllocation from, LAllocation to, LDefinition::Type type)
693
191
{
694
#ifdef DEBUG
695
    MOZ_ASSERT(from != to);
696
    for (size_t i = 0; i < moves_.length(); i++) {
697
        MOZ_ASSERT(to != moves_[i].to());
698
    }
699
700
    // Check that SIMD moves are aligned according to ABI requirements.
701
    if (LDefinition(type).isSimdType()) {
702
        MOZ_ASSERT(from.isMemory() || from.isFloatReg());
703
        if (from.isMemory()) {
704
            if (from.isArgument()) {
705
                MOZ_ASSERT(from.toArgument()->index() % SimdMemoryAlignment == 0);
706
            } else {
707
                MOZ_ASSERT(from.toStackSlot()->slot() % SimdMemoryAlignment == 0);
708
            }
709
        }
710
        MOZ_ASSERT(to.isMemory() || to.isFloatReg());
711
        if (to.isMemory()) {
712
            if (to.isArgument()) {
713
                MOZ_ASSERT(to.toArgument()->index() % SimdMemoryAlignment == 0);
714
            } else {
715
                MOZ_ASSERT(to.toStackSlot()->slot() % SimdMemoryAlignment == 0);
716
            }
717
        }
718
    }
719
#endif
720
    return moves_.append(LMove(from, to, type));
721
191
}
722
723
bool
724
LMoveGroup::addAfter(LAllocation from, LAllocation to, LDefinition::Type type)
725
5
{
726
5
    // Transform the operands to this move so that performing the result
727
5
    // simultaneously with existing moves in the group will have the same
728
5
    // effect as if the original move took place after the existing moves.
729
5
730
10
    for (size_t i = 0; i < moves_.length(); i++) {
731
5
        if (moves_[i].to() == from) {
732
0
            from = moves_[i].from();
733
0
            break;
734
0
        }
735
5
    }
736
5
737
5
    if (from == to) {
738
0
        return true;
739
0
    }
740
5
741
10
    for (size_t i = 0; i < moves_.length(); i++) {
742
5
        if (to == moves_[i].to()) {
743
0
            moves_[i] = LMove(from, to, type);
744
0
            return true;
745
0
        }
746
5
    }
747
5
748
5
    return add(from, to, type);
749
5
}
750
751
#ifdef JS_JITSPEW
752
void
753
LMoveGroup::printOperands(GenericPrinter& out)
754
{
755
    for (size_t i = 0; i < numMoves(); i++) {
756
        const LMove& move = getMove(i);
757
        out.printf(" [%s -> %s", move.from().toString().get(), move.to().toString().get());
758
        out.printf(", %s", DefTypeName(move.type()));
759
        out.printf("]");
760
        if (i != numMoves() - 1) {
761
            out.printf(",");
762
        }
763
    }
764
}
765
#endif
766
767
#define LIROP(x) static_assert(!std::is_polymorphic<L##x>::value, \
768
                               "LIR instructions should not have virtual methods");
769
    LIR_OPCODE_LIST(LIROP)
770
#undef LIROP