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