/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 |