|
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/IonSpewer.h" |
|
14 #include "jit/MIR.h" |
|
15 #include "jit/MIRGenerator.h" |
|
16 |
|
17 using namespace js; |
|
18 using namespace js::jit; |
|
19 |
|
20 LIRGraph::LIRGraph(MIRGraph *mir) |
|
21 : blocks_(mir->alloc()), |
|
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 osrBlock_(nullptr), |
|
32 mir_(*mir) |
|
33 { |
|
34 } |
|
35 |
|
36 bool |
|
37 LIRGraph::addConstantToPool(const Value &v, uint32_t *index) |
|
38 { |
|
39 JS_ASSERT(constantPoolMap_.initialized()); |
|
40 |
|
41 ConstantPoolMap::AddPtr p = constantPoolMap_.lookupForAdd(v); |
|
42 if (p) { |
|
43 *index = p->value(); |
|
44 return true; |
|
45 } |
|
46 *index = constantPool_.length(); |
|
47 return constantPool_.append(v) && constantPoolMap_.add(p, v, *index); |
|
48 } |
|
49 |
|
50 bool |
|
51 LIRGraph::noteNeedsSafepoint(LInstruction *ins) |
|
52 { |
|
53 // Instructions with safepoints must be in linear order. |
|
54 JS_ASSERT_IF(!safepoints_.empty(), safepoints_.back()->id() < ins->id()); |
|
55 if (!ins->isCall() && !nonCallSafepoints_.append(ins)) |
|
56 return false; |
|
57 return safepoints_.append(ins); |
|
58 } |
|
59 |
|
60 void |
|
61 LIRGraph::removeBlock(size_t i) |
|
62 { |
|
63 blocks_.erase(blocks_.begin() + i); |
|
64 } |
|
65 |
|
66 uint32_t |
|
67 LBlock::firstId() |
|
68 { |
|
69 if (phis_.length()) { |
|
70 return phis_[0]->id(); |
|
71 } else { |
|
72 for (LInstructionIterator i(instructions_.begin()); i != instructions_.end(); i++) { |
|
73 if (i->id()) |
|
74 return i->id(); |
|
75 } |
|
76 } |
|
77 return 0; |
|
78 } |
|
79 uint32_t |
|
80 LBlock::lastId() |
|
81 { |
|
82 LInstruction *last = *instructions_.rbegin(); |
|
83 JS_ASSERT(last->id()); |
|
84 // The last instruction is a control flow instruction which does not have |
|
85 // any output. |
|
86 JS_ASSERT(last->numDefs() == 0); |
|
87 return last->id(); |
|
88 } |
|
89 |
|
90 LMoveGroup * |
|
91 LBlock::getEntryMoveGroup(TempAllocator &alloc) |
|
92 { |
|
93 if (entryMoveGroup_) |
|
94 return entryMoveGroup_; |
|
95 entryMoveGroup_ = LMoveGroup::New(alloc); |
|
96 if (begin()->isLabel()) |
|
97 insertAfter(*begin(), entryMoveGroup_); |
|
98 else |
|
99 insertBefore(*begin(), entryMoveGroup_); |
|
100 return entryMoveGroup_; |
|
101 } |
|
102 |
|
103 LMoveGroup * |
|
104 LBlock::getExitMoveGroup(TempAllocator &alloc) |
|
105 { |
|
106 if (exitMoveGroup_) |
|
107 return exitMoveGroup_; |
|
108 exitMoveGroup_ = LMoveGroup::New(alloc); |
|
109 insertBefore(*rbegin(), exitMoveGroup_); |
|
110 return exitMoveGroup_; |
|
111 } |
|
112 |
|
113 static size_t |
|
114 TotalOperandCount(MResumePoint *mir) |
|
115 { |
|
116 size_t accum = mir->numOperands(); |
|
117 while ((mir = mir->caller())) |
|
118 accum += mir->numOperands(); |
|
119 return accum; |
|
120 } |
|
121 |
|
122 LRecoverInfo::LRecoverInfo(TempAllocator &alloc) |
|
123 : instructions_(alloc), |
|
124 recoverOffset_(INVALID_RECOVER_OFFSET) |
|
125 { } |
|
126 |
|
127 LRecoverInfo * |
|
128 LRecoverInfo::New(MIRGenerator *gen, MResumePoint *mir) |
|
129 { |
|
130 LRecoverInfo *recoverInfo = new(gen->alloc()) LRecoverInfo(gen->alloc()); |
|
131 if (!recoverInfo || !recoverInfo->init(mir)) |
|
132 return nullptr; |
|
133 |
|
134 IonSpew(IonSpew_Snapshots, "Generating LIR recover info %p from MIR (%p)", |
|
135 (void *)recoverInfo, (void *)mir); |
|
136 |
|
137 return recoverInfo; |
|
138 } |
|
139 |
|
140 bool |
|
141 LRecoverInfo::init(MResumePoint *rp) |
|
142 { |
|
143 MResumePoint *it = rp; |
|
144 |
|
145 // Sort operations in the order in which we need to restore the stack. This |
|
146 // implies that outer frames, as well as operations needed to recover the |
|
147 // current frame, are located before the current frame. The inner-most |
|
148 // resume point should be the last element in the list. |
|
149 do { |
|
150 if (!instructions_.append(it)) |
|
151 return false; |
|
152 it = it->caller(); |
|
153 } while (it); |
|
154 |
|
155 Reverse(instructions_.begin(), instructions_.end()); |
|
156 MOZ_ASSERT(mir() == rp); |
|
157 return true; |
|
158 } |
|
159 |
|
160 LSnapshot::LSnapshot(LRecoverInfo *recoverInfo, BailoutKind kind) |
|
161 : numSlots_(TotalOperandCount(recoverInfo->mir()) * BOX_PIECES), |
|
162 slots_(nullptr), |
|
163 recoverInfo_(recoverInfo), |
|
164 snapshotOffset_(INVALID_SNAPSHOT_OFFSET), |
|
165 bailoutId_(INVALID_BAILOUT_ID), |
|
166 bailoutKind_(kind) |
|
167 { } |
|
168 |
|
169 bool |
|
170 LSnapshot::init(MIRGenerator *gen) |
|
171 { |
|
172 slots_ = gen->allocate<LAllocation>(numSlots_); |
|
173 return !!slots_; |
|
174 } |
|
175 |
|
176 LSnapshot * |
|
177 LSnapshot::New(MIRGenerator *gen, LRecoverInfo *recover, BailoutKind kind) |
|
178 { |
|
179 LSnapshot *snapshot = new(gen->alloc()) LSnapshot(recover, kind); |
|
180 if (!snapshot || !snapshot->init(gen)) |
|
181 return nullptr; |
|
182 |
|
183 IonSpew(IonSpew_Snapshots, "Generating LIR snapshot %p from recover (%p)", |
|
184 (void *)snapshot, (void *)recover); |
|
185 |
|
186 return snapshot; |
|
187 } |
|
188 |
|
189 void |
|
190 LSnapshot::rewriteRecoveredInput(LUse input) |
|
191 { |
|
192 // Mark any operands to this snapshot with the same value as input as being |
|
193 // equal to the instruction's result. |
|
194 for (size_t i = 0; i < numEntries(); i++) { |
|
195 if (getEntry(i)->isUse() && getEntry(i)->toUse()->virtualRegister() == input.virtualRegister()) |
|
196 setEntry(i, LUse(input.virtualRegister(), LUse::RECOVERED_INPUT)); |
|
197 } |
|
198 } |
|
199 |
|
200 LPhi * |
|
201 LPhi::New(MIRGenerator *gen, MPhi *ins) |
|
202 { |
|
203 LPhi *phi = new (gen->alloc()) LPhi(); |
|
204 LAllocation *inputs = gen->allocate<LAllocation>(ins->numOperands()); |
|
205 if (!inputs) |
|
206 return nullptr; |
|
207 |
|
208 phi->inputs_ = inputs; |
|
209 phi->setMir(ins); |
|
210 return phi; |
|
211 } |
|
212 |
|
213 void |
|
214 LInstruction::printName(FILE *fp, Opcode op) |
|
215 { |
|
216 static const char * const names[] = |
|
217 { |
|
218 #define LIROP(x) #x, |
|
219 LIR_OPCODE_LIST(LIROP) |
|
220 #undef LIROP |
|
221 }; |
|
222 const char *name = names[op]; |
|
223 size_t len = strlen(name); |
|
224 for (size_t i = 0; i < len; i++) |
|
225 fprintf(fp, "%c", tolower(name[i])); |
|
226 } |
|
227 |
|
228 void |
|
229 LInstruction::printName(FILE *fp) |
|
230 { |
|
231 printName(fp, op()); |
|
232 } |
|
233 |
|
234 static const char * const TypeChars[] = |
|
235 { |
|
236 "g", // GENERAL |
|
237 "i", // INT32 |
|
238 "o", // OBJECT |
|
239 "s", // SLOTS |
|
240 "f", // FLOAT32 |
|
241 "d", // DOUBLE |
|
242 #ifdef JS_NUNBOX32 |
|
243 "t", // TYPE |
|
244 "p" // PAYLOAD |
|
245 #elif JS_PUNBOX64 |
|
246 "x" // BOX |
|
247 #endif |
|
248 }; |
|
249 |
|
250 static void |
|
251 PrintDefinition(FILE *fp, const LDefinition &def) |
|
252 { |
|
253 fprintf(fp, "[%s", TypeChars[def.type()]); |
|
254 if (def.virtualRegister()) |
|
255 fprintf(fp, ":%d", def.virtualRegister()); |
|
256 if (def.policy() == LDefinition::PRESET) { |
|
257 fprintf(fp, " (%s)", def.output()->toString()); |
|
258 } else if (def.policy() == LDefinition::MUST_REUSE_INPUT) { |
|
259 fprintf(fp, " (!)"); |
|
260 } else if (def.policy() == LDefinition::PASSTHROUGH) { |
|
261 fprintf(fp, " (-)"); |
|
262 } |
|
263 fprintf(fp, "]"); |
|
264 } |
|
265 |
|
266 #ifdef DEBUG |
|
267 static void |
|
268 PrintUse(char *buf, size_t size, const LUse *use) |
|
269 { |
|
270 switch (use->policy()) { |
|
271 case LUse::REGISTER: |
|
272 JS_snprintf(buf, size, "v%d:r", use->virtualRegister()); |
|
273 break; |
|
274 case LUse::FIXED: |
|
275 // Unfortunately, we don't know here whether the virtual register is a |
|
276 // float or a double. Should we steal a bit in LUse for help? For now, |
|
277 // nothing defines any fixed xmm registers. |
|
278 JS_snprintf(buf, size, "v%d:%s", use->virtualRegister(), |
|
279 Registers::GetName(Registers::Code(use->registerCode()))); |
|
280 break; |
|
281 case LUse::ANY: |
|
282 JS_snprintf(buf, size, "v%d:r?", use->virtualRegister()); |
|
283 break; |
|
284 case LUse::KEEPALIVE: |
|
285 JS_snprintf(buf, size, "v%d:*", use->virtualRegister()); |
|
286 break; |
|
287 case LUse::RECOVERED_INPUT: |
|
288 JS_snprintf(buf, size, "v%d:**", use->virtualRegister()); |
|
289 break; |
|
290 default: |
|
291 MOZ_ASSUME_UNREACHABLE("invalid use policy"); |
|
292 } |
|
293 } |
|
294 |
|
295 const char * |
|
296 LAllocation::toString() const |
|
297 { |
|
298 // Not reentrant! |
|
299 static char buf[40]; |
|
300 |
|
301 switch (kind()) { |
|
302 case LAllocation::CONSTANT_VALUE: |
|
303 case LAllocation::CONSTANT_INDEX: |
|
304 return "c"; |
|
305 case LAllocation::GPR: |
|
306 JS_snprintf(buf, sizeof(buf), "=%s", toGeneralReg()->reg().name()); |
|
307 return buf; |
|
308 case LAllocation::FPU: |
|
309 JS_snprintf(buf, sizeof(buf), "=%s", toFloatReg()->reg().name()); |
|
310 return buf; |
|
311 case LAllocation::STACK_SLOT: |
|
312 JS_snprintf(buf, sizeof(buf), "stack:%d", toStackSlot()->slot()); |
|
313 return buf; |
|
314 case LAllocation::ARGUMENT_SLOT: |
|
315 JS_snprintf(buf, sizeof(buf), "arg:%d", toArgument()->index()); |
|
316 return buf; |
|
317 case LAllocation::USE: |
|
318 PrintUse(buf, sizeof(buf), toUse()); |
|
319 return buf; |
|
320 default: |
|
321 MOZ_ASSUME_UNREACHABLE("what?"); |
|
322 } |
|
323 } |
|
324 #endif // DEBUG |
|
325 |
|
326 void |
|
327 LAllocation::dump() const |
|
328 { |
|
329 fprintf(stderr, "%s\n", toString()); |
|
330 } |
|
331 |
|
332 void |
|
333 LInstruction::printOperands(FILE *fp) |
|
334 { |
|
335 for (size_t i = 0, e = numOperands(); i < e; i++) { |
|
336 fprintf(fp, " (%s)", getOperand(i)->toString()); |
|
337 if (i != numOperands() - 1) |
|
338 fprintf(fp, ","); |
|
339 } |
|
340 } |
|
341 |
|
342 void |
|
343 LInstruction::assignSnapshot(LSnapshot *snapshot) |
|
344 { |
|
345 JS_ASSERT(!snapshot_); |
|
346 snapshot_ = snapshot; |
|
347 |
|
348 #ifdef DEBUG |
|
349 if (IonSpewEnabled(IonSpew_Snapshots)) { |
|
350 IonSpewHeader(IonSpew_Snapshots); |
|
351 fprintf(IonSpewFile, "Assigning snapshot %p to instruction %p (", |
|
352 (void *)snapshot, (void *)this); |
|
353 printName(IonSpewFile); |
|
354 fprintf(IonSpewFile, ")\n"); |
|
355 } |
|
356 #endif |
|
357 } |
|
358 |
|
359 void |
|
360 LInstruction::dump(FILE *fp) |
|
361 { |
|
362 fprintf(fp, "{"); |
|
363 for (size_t i = 0; i < numDefs(); i++) { |
|
364 PrintDefinition(fp, *getDef(i)); |
|
365 if (i != numDefs() - 1) |
|
366 fprintf(fp, ", "); |
|
367 } |
|
368 fprintf(fp, "} <- "); |
|
369 |
|
370 printName(fp); |
|
371 |
|
372 |
|
373 printInfo(fp); |
|
374 |
|
375 if (numTemps()) { |
|
376 fprintf(fp, " t=("); |
|
377 for (size_t i = 0; i < numTemps(); i++) { |
|
378 PrintDefinition(fp, *getTemp(i)); |
|
379 if (i != numTemps() - 1) |
|
380 fprintf(fp, ", "); |
|
381 } |
|
382 fprintf(fp, ")"); |
|
383 } |
|
384 fprintf(fp, "\n"); |
|
385 } |
|
386 |
|
387 void |
|
388 LInstruction::dump() |
|
389 { |
|
390 return dump(stderr); |
|
391 } |
|
392 |
|
393 void |
|
394 LInstruction::initSafepoint(TempAllocator &alloc) |
|
395 { |
|
396 JS_ASSERT(!safepoint_); |
|
397 safepoint_ = new(alloc) LSafepoint(alloc); |
|
398 JS_ASSERT(safepoint_); |
|
399 } |
|
400 |
|
401 bool |
|
402 LMoveGroup::add(LAllocation *from, LAllocation *to, LDefinition::Type type) |
|
403 { |
|
404 #ifdef DEBUG |
|
405 JS_ASSERT(*from != *to); |
|
406 for (size_t i = 0; i < moves_.length(); i++) |
|
407 JS_ASSERT(*to != *moves_[i].to()); |
|
408 #endif |
|
409 return moves_.append(LMove(from, to, type)); |
|
410 } |
|
411 |
|
412 bool |
|
413 LMoveGroup::addAfter(LAllocation *from, LAllocation *to, LDefinition::Type type) |
|
414 { |
|
415 // Transform the operands to this move so that performing the result |
|
416 // simultaneously with existing moves in the group will have the same |
|
417 // effect as if the original move took place after the existing moves. |
|
418 |
|
419 for (size_t i = 0; i < moves_.length(); i++) { |
|
420 if (*moves_[i].to() == *from) { |
|
421 from = moves_[i].from(); |
|
422 break; |
|
423 } |
|
424 } |
|
425 |
|
426 if (*from == *to) |
|
427 return true; |
|
428 |
|
429 for (size_t i = 0; i < moves_.length(); i++) { |
|
430 if (*to == *moves_[i].to()) { |
|
431 moves_[i] = LMove(from, to, type); |
|
432 return true; |
|
433 } |
|
434 } |
|
435 |
|
436 return add(from, to, type); |
|
437 } |
|
438 |
|
439 void |
|
440 LMoveGroup::printOperands(FILE *fp) |
|
441 { |
|
442 for (size_t i = 0; i < numMoves(); i++) { |
|
443 const LMove &move = getMove(i); |
|
444 // Use two printfs, as LAllocation::toString is not reentrant. |
|
445 fprintf(fp, "[%s", move.from()->toString()); |
|
446 fprintf(fp, " -> %s]", move.to()->toString()); |
|
447 if (i != numMoves() - 1) |
|
448 fprintf(fp, ", "); |
|
449 } |
|
450 } |