|
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/shared/CodeGenerator-shared-inl.h" |
|
8 |
|
9 #include "mozilla/DebugOnly.h" |
|
10 |
|
11 #include "jit/IonCaches.h" |
|
12 #include "jit/IonMacroAssembler.h" |
|
13 #include "jit/IonSpewer.h" |
|
14 #include "jit/MIR.h" |
|
15 #include "jit/MIRGenerator.h" |
|
16 #include "jit/ParallelFunctions.h" |
|
17 #include "vm/TraceLogging.h" |
|
18 |
|
19 #include "jit/IonFrames-inl.h" |
|
20 |
|
21 using namespace js; |
|
22 using namespace js::jit; |
|
23 |
|
24 using mozilla::DebugOnly; |
|
25 |
|
26 namespace js { |
|
27 namespace jit { |
|
28 |
|
29 MacroAssembler & |
|
30 CodeGeneratorShared::ensureMasm(MacroAssembler *masmArg) |
|
31 { |
|
32 if (masmArg) |
|
33 return *masmArg; |
|
34 maybeMasm_.construct(); |
|
35 return maybeMasm_.ref(); |
|
36 } |
|
37 |
|
38 CodeGeneratorShared::CodeGeneratorShared(MIRGenerator *gen, LIRGraph *graph, MacroAssembler *masmArg) |
|
39 : oolIns(nullptr), |
|
40 maybeMasm_(), |
|
41 masm(ensureMasm(masmArg)), |
|
42 gen(gen), |
|
43 graph(*graph), |
|
44 current(nullptr), |
|
45 snapshots_(), |
|
46 recovers_(), |
|
47 deoptTable_(nullptr), |
|
48 #ifdef DEBUG |
|
49 pushedArgs_(0), |
|
50 #endif |
|
51 lastOsiPointOffset_(0), |
|
52 sps_(&GetIonContext()->runtime->spsProfiler(), &lastPC_), |
|
53 osrEntryOffset_(0), |
|
54 skipArgCheckEntryOffset_(0), |
|
55 frameDepth_(graph->paddedLocalSlotsSize() + graph->argumentsSize()) |
|
56 { |
|
57 if (!gen->compilingAsmJS()) |
|
58 masm.setInstrumentation(&sps_); |
|
59 |
|
60 // Since asm.js uses the system ABI which does not necessarily use a |
|
61 // regular array where all slots are sizeof(Value), it maintains the max |
|
62 // argument stack depth separately. |
|
63 if (gen->compilingAsmJS()) { |
|
64 JS_ASSERT(graph->argumentSlotCount() == 0); |
|
65 frameDepth_ += gen->maxAsmJSStackArgBytes(); |
|
66 |
|
67 // An MAsmJSCall does not align the stack pointer at calls sites but instead |
|
68 // relies on the a priori stack adjustment (in the prologue) on platforms |
|
69 // (like x64) which require the stack to be aligned. |
|
70 #if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS) |
|
71 bool forceAlign = true; |
|
72 #else |
|
73 bool forceAlign = false; |
|
74 #endif |
|
75 if (gen->performsAsmJSCall() || forceAlign) { |
|
76 unsigned alignmentAtCall = AlignmentMidPrologue + frameDepth_; |
|
77 if (unsigned rem = alignmentAtCall % StackAlignment) |
|
78 frameDepth_ += StackAlignment - rem; |
|
79 } |
|
80 |
|
81 // FrameSizeClass is only used for bailing, which cannot happen in |
|
82 // asm.js code. |
|
83 frameClass_ = FrameSizeClass::None(); |
|
84 } else { |
|
85 frameClass_ = FrameSizeClass::FromDepth(frameDepth_); |
|
86 } |
|
87 } |
|
88 |
|
89 bool |
|
90 CodeGeneratorShared::generateOutOfLineCode() |
|
91 { |
|
92 for (size_t i = 0; i < outOfLineCode_.length(); i++) { |
|
93 if (!gen->alloc().ensureBallast()) |
|
94 return false; |
|
95 masm.setFramePushed(outOfLineCode_[i]->framePushed()); |
|
96 lastPC_ = outOfLineCode_[i]->pc(); |
|
97 if (!sps_.prepareForOOL()) |
|
98 return false; |
|
99 sps_.setPushed(outOfLineCode_[i]->script()); |
|
100 outOfLineCode_[i]->bind(&masm); |
|
101 |
|
102 oolIns = outOfLineCode_[i]; |
|
103 if (!outOfLineCode_[i]->generate(this)) |
|
104 return false; |
|
105 sps_.finishOOL(); |
|
106 } |
|
107 oolIns = nullptr; |
|
108 |
|
109 return true; |
|
110 } |
|
111 |
|
112 bool |
|
113 CodeGeneratorShared::addOutOfLineCode(OutOfLineCode *code) |
|
114 { |
|
115 code->setFramePushed(masm.framePushed()); |
|
116 // If an OOL instruction adds another OOL instruction, then use the original |
|
117 // instruction's script/pc instead of the basic block's that we're on |
|
118 // because they're probably not relevant any more. |
|
119 if (oolIns) |
|
120 code->setSource(oolIns->script(), oolIns->pc()); |
|
121 else |
|
122 code->setSource(current ? current->mir()->info().script() : nullptr, lastPC_); |
|
123 JS_ASSERT_IF(code->script(), code->script()->containsPC(code->pc())); |
|
124 return outOfLineCode_.append(code); |
|
125 } |
|
126 |
|
127 // see OffsetOfFrameSlot |
|
128 static inline int32_t |
|
129 ToStackIndex(LAllocation *a) |
|
130 { |
|
131 if (a->isStackSlot()) { |
|
132 JS_ASSERT(a->toStackSlot()->slot() >= 1); |
|
133 return a->toStackSlot()->slot(); |
|
134 } |
|
135 JS_ASSERT(-int32_t(sizeof(IonJSFrameLayout)) <= a->toArgument()->index()); |
|
136 return -int32_t(sizeof(IonJSFrameLayout) + a->toArgument()->index()); |
|
137 } |
|
138 |
|
139 bool |
|
140 CodeGeneratorShared::encodeAllocations(LSnapshot *snapshot, MResumePoint *resumePoint, |
|
141 uint32_t *startIndex) |
|
142 { |
|
143 IonSpew(IonSpew_Codegen, "Encoding %u of resume point %p's operands starting from %u", |
|
144 resumePoint->numOperands(), (void *) resumePoint, *startIndex); |
|
145 for (uint32_t allocno = 0, e = resumePoint->numOperands(); allocno < e; allocno++) { |
|
146 uint32_t i = allocno + *startIndex; |
|
147 MDefinition *mir = resumePoint->getOperand(allocno); |
|
148 |
|
149 if (mir->isBox()) |
|
150 mir = mir->toBox()->getOperand(0); |
|
151 |
|
152 MIRType type = mir->isUnused() |
|
153 ? MIRType_MagicOptimizedOut |
|
154 : mir->type(); |
|
155 |
|
156 RValueAllocation alloc; |
|
157 |
|
158 switch (type) { |
|
159 case MIRType_Undefined: |
|
160 alloc = RValueAllocation::Undefined(); |
|
161 break; |
|
162 case MIRType_Null: |
|
163 alloc = RValueAllocation::Null(); |
|
164 break; |
|
165 case MIRType_Int32: |
|
166 case MIRType_String: |
|
167 case MIRType_Object: |
|
168 case MIRType_Boolean: |
|
169 case MIRType_Double: |
|
170 case MIRType_Float32: |
|
171 { |
|
172 LAllocation *payload = snapshot->payloadOfSlot(i); |
|
173 JSValueType valueType = ValueTypeFromMIRType(type); |
|
174 if (payload->isMemory()) { |
|
175 if (type == MIRType_Float32) |
|
176 alloc = RValueAllocation::Float32(ToStackIndex(payload)); |
|
177 else |
|
178 alloc = RValueAllocation::Typed(valueType, ToStackIndex(payload)); |
|
179 } else if (payload->isGeneralReg()) { |
|
180 alloc = RValueAllocation::Typed(valueType, ToRegister(payload)); |
|
181 } else if (payload->isFloatReg()) { |
|
182 FloatRegister reg = ToFloatRegister(payload); |
|
183 if (type == MIRType_Float32) |
|
184 alloc = RValueAllocation::Float32(reg); |
|
185 else |
|
186 alloc = RValueAllocation::Double(reg); |
|
187 } else { |
|
188 MConstant *constant = mir->toConstant(); |
|
189 uint32_t index; |
|
190 if (!graph.addConstantToPool(constant->value(), &index)) |
|
191 return false; |
|
192 alloc = RValueAllocation::ConstantPool(index); |
|
193 } |
|
194 break; |
|
195 } |
|
196 case MIRType_MagicOptimizedArguments: |
|
197 case MIRType_MagicOptimizedOut: |
|
198 { |
|
199 uint32_t index; |
|
200 JSWhyMagic why = (type == MIRType_MagicOptimizedArguments |
|
201 ? JS_OPTIMIZED_ARGUMENTS |
|
202 : JS_OPTIMIZED_OUT); |
|
203 Value v = MagicValue(why); |
|
204 if (!graph.addConstantToPool(v, &index)) |
|
205 return false; |
|
206 alloc = RValueAllocation::ConstantPool(index); |
|
207 break; |
|
208 } |
|
209 default: |
|
210 { |
|
211 JS_ASSERT(mir->type() == MIRType_Value); |
|
212 LAllocation *payload = snapshot->payloadOfSlot(i); |
|
213 #ifdef JS_NUNBOX32 |
|
214 LAllocation *type = snapshot->typeOfSlot(i); |
|
215 if (type->isRegister()) { |
|
216 if (payload->isRegister()) |
|
217 alloc = RValueAllocation::Untyped(ToRegister(type), ToRegister(payload)); |
|
218 else |
|
219 alloc = RValueAllocation::Untyped(ToRegister(type), ToStackIndex(payload)); |
|
220 } else { |
|
221 if (payload->isRegister()) |
|
222 alloc = RValueAllocation::Untyped(ToStackIndex(type), ToRegister(payload)); |
|
223 else |
|
224 alloc = RValueAllocation::Untyped(ToStackIndex(type), ToStackIndex(payload)); |
|
225 } |
|
226 #elif JS_PUNBOX64 |
|
227 if (payload->isRegister()) |
|
228 alloc = RValueAllocation::Untyped(ToRegister(payload)); |
|
229 else |
|
230 alloc = RValueAllocation::Untyped(ToStackIndex(payload)); |
|
231 #endif |
|
232 break; |
|
233 } |
|
234 } |
|
235 |
|
236 snapshots_.add(alloc); |
|
237 } |
|
238 |
|
239 *startIndex += resumePoint->numOperands(); |
|
240 return true; |
|
241 } |
|
242 |
|
243 bool |
|
244 CodeGeneratorShared::encode(LRecoverInfo *recover) |
|
245 { |
|
246 if (recover->recoverOffset() != INVALID_RECOVER_OFFSET) |
|
247 return true; |
|
248 |
|
249 uint32_t frameCount = recover->mir()->frameCount(); |
|
250 IonSpew(IonSpew_Snapshots, "Encoding LRecoverInfo %p (frameCount %u)", |
|
251 (void *)recover, frameCount); |
|
252 |
|
253 MResumePoint::Mode mode = recover->mir()->mode(); |
|
254 JS_ASSERT(mode != MResumePoint::Outer); |
|
255 bool resumeAfter = (mode == MResumePoint::ResumeAfter); |
|
256 |
|
257 RecoverOffset offset = recovers_.startRecover(frameCount, resumeAfter); |
|
258 |
|
259 for (MResumePoint **it = recover->begin(), **end = recover->end(); |
|
260 it != end; |
|
261 ++it) |
|
262 { |
|
263 if (!recovers_.writeFrame(*it)) |
|
264 return false; |
|
265 } |
|
266 |
|
267 recovers_.endRecover(); |
|
268 recover->setRecoverOffset(offset); |
|
269 return !recovers_.oom(); |
|
270 } |
|
271 |
|
272 bool |
|
273 CodeGeneratorShared::encode(LSnapshot *snapshot) |
|
274 { |
|
275 if (snapshot->snapshotOffset() != INVALID_SNAPSHOT_OFFSET) |
|
276 return true; |
|
277 |
|
278 LRecoverInfo *recoverInfo = snapshot->recoverInfo(); |
|
279 if (!encode(recoverInfo)) |
|
280 return false; |
|
281 |
|
282 RecoverOffset recoverOffset = recoverInfo->recoverOffset(); |
|
283 MOZ_ASSERT(recoverOffset != INVALID_RECOVER_OFFSET); |
|
284 |
|
285 IonSpew(IonSpew_Snapshots, "Encoding LSnapshot %p (LRecover %p)", |
|
286 (void *)snapshot, (void*) recoverInfo); |
|
287 |
|
288 SnapshotOffset offset = snapshots_.startSnapshot(recoverOffset, snapshot->bailoutKind()); |
|
289 |
|
290 #ifdef TRACK_SNAPSHOTS |
|
291 uint32_t pcOpcode = 0; |
|
292 uint32_t lirOpcode = 0; |
|
293 uint32_t lirId = 0; |
|
294 uint32_t mirOpcode = 0; |
|
295 uint32_t mirId = 0; |
|
296 |
|
297 if (LInstruction *ins = instruction()) { |
|
298 lirOpcode = ins->op(); |
|
299 lirId = ins->id(); |
|
300 if (ins->mirRaw()) { |
|
301 mirOpcode = ins->mirRaw()->op(); |
|
302 mirId = ins->mirRaw()->id(); |
|
303 if (ins->mirRaw()->trackedPc()) |
|
304 pcOpcode = *ins->mirRaw()->trackedPc(); |
|
305 } |
|
306 } |
|
307 snapshots_.trackSnapshot(pcOpcode, mirOpcode, mirId, lirOpcode, lirId); |
|
308 #endif |
|
309 |
|
310 uint32_t startIndex = 0; |
|
311 for (MResumePoint **it = recoverInfo->begin(), **end = recoverInfo->end(); |
|
312 it != end; |
|
313 ++it) |
|
314 { |
|
315 MResumePoint *mir = *it; |
|
316 if (!encodeAllocations(snapshot, mir, &startIndex)) |
|
317 return false; |
|
318 } |
|
319 |
|
320 MOZ_ASSERT(snapshots_.allocWritten() == snapshot->numSlots()); |
|
321 snapshots_.endSnapshot(); |
|
322 snapshot->setSnapshotOffset(offset); |
|
323 return !snapshots_.oom(); |
|
324 } |
|
325 |
|
326 bool |
|
327 CodeGeneratorShared::assignBailoutId(LSnapshot *snapshot) |
|
328 { |
|
329 JS_ASSERT(snapshot->snapshotOffset() != INVALID_SNAPSHOT_OFFSET); |
|
330 |
|
331 // Can we not use bailout tables at all? |
|
332 if (!deoptTable_) |
|
333 return false; |
|
334 |
|
335 JS_ASSERT(frameClass_ != FrameSizeClass::None()); |
|
336 |
|
337 if (snapshot->bailoutId() != INVALID_BAILOUT_ID) |
|
338 return true; |
|
339 |
|
340 // Is the bailout table full? |
|
341 if (bailouts_.length() >= BAILOUT_TABLE_SIZE) |
|
342 return false; |
|
343 |
|
344 unsigned bailoutId = bailouts_.length(); |
|
345 snapshot->setBailoutId(bailoutId); |
|
346 IonSpew(IonSpew_Snapshots, "Assigned snapshot bailout id %u", bailoutId); |
|
347 return bailouts_.append(snapshot->snapshotOffset()); |
|
348 } |
|
349 |
|
350 void |
|
351 CodeGeneratorShared::encodeSafepoints() |
|
352 { |
|
353 for (SafepointIndex *it = safepointIndices_.begin(), *end = safepointIndices_.end(); |
|
354 it != end; |
|
355 ++it) |
|
356 { |
|
357 LSafepoint *safepoint = it->safepoint(); |
|
358 |
|
359 if (!safepoint->encoded()) { |
|
360 safepoint->fixupOffset(&masm); |
|
361 safepoints_.encode(safepoint); |
|
362 } |
|
363 |
|
364 it->resolve(); |
|
365 } |
|
366 } |
|
367 |
|
368 bool |
|
369 CodeGeneratorShared::markSafepoint(LInstruction *ins) |
|
370 { |
|
371 return markSafepointAt(masm.currentOffset(), ins); |
|
372 } |
|
373 |
|
374 bool |
|
375 CodeGeneratorShared::markSafepointAt(uint32_t offset, LInstruction *ins) |
|
376 { |
|
377 JS_ASSERT_IF(!safepointIndices_.empty(), |
|
378 offset - safepointIndices_.back().displacement() >= sizeof(uint32_t)); |
|
379 return safepointIndices_.append(SafepointIndex(offset, ins->safepoint())); |
|
380 } |
|
381 |
|
382 void |
|
383 CodeGeneratorShared::ensureOsiSpace() |
|
384 { |
|
385 // For a refresher, an invalidation point is of the form: |
|
386 // 1: call <target> |
|
387 // 2: ... |
|
388 // 3: <osipoint> |
|
389 // |
|
390 // The four bytes *before* instruction 2 are overwritten with an offset. |
|
391 // Callers must ensure that the instruction itself has enough bytes to |
|
392 // support this. |
|
393 // |
|
394 // The bytes *at* instruction 3 are overwritten with an invalidation jump. |
|
395 // jump. These bytes may be in a completely different IR sequence, but |
|
396 // represent the join point of the call out of the function. |
|
397 // |
|
398 // At points where we want to ensure that invalidation won't corrupt an |
|
399 // important instruction, we make sure to pad with nops. |
|
400 if (masm.currentOffset() - lastOsiPointOffset_ < Assembler::patchWrite_NearCallSize()) { |
|
401 int32_t paddingSize = Assembler::patchWrite_NearCallSize(); |
|
402 paddingSize -= masm.currentOffset() - lastOsiPointOffset_; |
|
403 for (int32_t i = 0; i < paddingSize; ++i) |
|
404 masm.nop(); |
|
405 } |
|
406 JS_ASSERT(masm.currentOffset() - lastOsiPointOffset_ >= Assembler::patchWrite_NearCallSize()); |
|
407 lastOsiPointOffset_ = masm.currentOffset(); |
|
408 } |
|
409 |
|
410 bool |
|
411 CodeGeneratorShared::markOsiPoint(LOsiPoint *ins, uint32_t *callPointOffset) |
|
412 { |
|
413 if (!encode(ins->snapshot())) |
|
414 return false; |
|
415 |
|
416 ensureOsiSpace(); |
|
417 |
|
418 *callPointOffset = masm.currentOffset(); |
|
419 SnapshotOffset so = ins->snapshot()->snapshotOffset(); |
|
420 return osiIndices_.append(OsiIndex(*callPointOffset, so)); |
|
421 } |
|
422 |
|
423 #ifdef CHECK_OSIPOINT_REGISTERS |
|
424 template <class Op> |
|
425 static void |
|
426 HandleRegisterDump(Op op, MacroAssembler &masm, RegisterSet liveRegs, Register activation, |
|
427 Register scratch) |
|
428 { |
|
429 const size_t baseOffset = JitActivation::offsetOfRegs(); |
|
430 |
|
431 // Handle live GPRs. |
|
432 for (GeneralRegisterIterator iter(liveRegs.gprs()); iter.more(); iter++) { |
|
433 Register reg = *iter; |
|
434 Address dump(activation, baseOffset + RegisterDump::offsetOfRegister(reg)); |
|
435 |
|
436 if (reg == activation) { |
|
437 // To use the original value of the activation register (that's |
|
438 // now on top of the stack), we need the scratch register. |
|
439 masm.push(scratch); |
|
440 masm.loadPtr(Address(StackPointer, sizeof(uintptr_t)), scratch); |
|
441 op(scratch, dump); |
|
442 masm.pop(scratch); |
|
443 } else { |
|
444 op(reg, dump); |
|
445 } |
|
446 } |
|
447 |
|
448 // Handle live FPRs. |
|
449 for (FloatRegisterIterator iter(liveRegs.fpus()); iter.more(); iter++) { |
|
450 FloatRegister reg = *iter; |
|
451 Address dump(activation, baseOffset + RegisterDump::offsetOfRegister(reg)); |
|
452 op(reg, dump); |
|
453 } |
|
454 } |
|
455 |
|
456 class StoreOp |
|
457 { |
|
458 MacroAssembler &masm; |
|
459 |
|
460 public: |
|
461 StoreOp(MacroAssembler &masm) |
|
462 : masm(masm) |
|
463 {} |
|
464 |
|
465 void operator()(Register reg, Address dump) { |
|
466 masm.storePtr(reg, dump); |
|
467 } |
|
468 void operator()(FloatRegister reg, Address dump) { |
|
469 masm.storeDouble(reg, dump); |
|
470 } |
|
471 }; |
|
472 |
|
473 static void |
|
474 StoreAllLiveRegs(MacroAssembler &masm, RegisterSet liveRegs) |
|
475 { |
|
476 // Store a copy of all live registers before performing the call. |
|
477 // When we reach the OsiPoint, we can use this to check nothing |
|
478 // modified them in the meantime. |
|
479 |
|
480 // Load pointer to the JitActivation in a scratch register. |
|
481 GeneralRegisterSet allRegs(GeneralRegisterSet::All()); |
|
482 Register scratch = allRegs.takeAny(); |
|
483 masm.push(scratch); |
|
484 masm.loadJitActivation(scratch); |
|
485 |
|
486 Address checkRegs(scratch, JitActivation::offsetOfCheckRegs()); |
|
487 masm.add32(Imm32(1), checkRegs); |
|
488 |
|
489 StoreOp op(masm); |
|
490 HandleRegisterDump<StoreOp>(op, masm, liveRegs, scratch, allRegs.getAny()); |
|
491 |
|
492 masm.pop(scratch); |
|
493 } |
|
494 |
|
495 class VerifyOp |
|
496 { |
|
497 MacroAssembler &masm; |
|
498 Label *failure_; |
|
499 |
|
500 public: |
|
501 VerifyOp(MacroAssembler &masm, Label *failure) |
|
502 : masm(masm), failure_(failure) |
|
503 {} |
|
504 |
|
505 void operator()(Register reg, Address dump) { |
|
506 masm.branchPtr(Assembler::NotEqual, dump, reg, failure_); |
|
507 } |
|
508 void operator()(FloatRegister reg, Address dump) { |
|
509 masm.loadDouble(dump, ScratchFloatReg); |
|
510 masm.branchDouble(Assembler::DoubleNotEqual, ScratchFloatReg, reg, failure_); |
|
511 } |
|
512 }; |
|
513 |
|
514 static void |
|
515 OsiPointRegisterCheckFailed() |
|
516 { |
|
517 // Any live register captured by a safepoint (other than temp registers) |
|
518 // must remain unchanged between the call and the OsiPoint instruction. |
|
519 MOZ_ASSUME_UNREACHABLE("Modified registers between VM call and OsiPoint"); |
|
520 } |
|
521 |
|
522 void |
|
523 CodeGeneratorShared::verifyOsiPointRegs(LSafepoint *safepoint) |
|
524 { |
|
525 // Ensure the live registers stored by callVM did not change between |
|
526 // the call and this OsiPoint. Try-catch relies on this invariant. |
|
527 |
|
528 // Load pointer to the JitActivation in a scratch register. |
|
529 GeneralRegisterSet allRegs(GeneralRegisterSet::All()); |
|
530 Register scratch = allRegs.takeAny(); |
|
531 masm.push(scratch); |
|
532 masm.loadJitActivation(scratch); |
|
533 |
|
534 // If we should not check registers (because the instruction did not call |
|
535 // into the VM, or a GC happened), we're done. |
|
536 Label failure, done; |
|
537 Address checkRegs(scratch, JitActivation::offsetOfCheckRegs()); |
|
538 masm.branch32(Assembler::Equal, checkRegs, Imm32(0), &done); |
|
539 |
|
540 // Having more than one VM function call made in one visit function at |
|
541 // runtime is a sec-ciritcal error, because if we conservatively assume that |
|
542 // one of the function call can re-enter Ion, then the invalidation process |
|
543 // will potentially add a call at a random location, by patching the code |
|
544 // before the return address. |
|
545 masm.branch32(Assembler::NotEqual, checkRegs, Imm32(1), &failure); |
|
546 |
|
547 // Ignore clobbered registers. Some instructions (like LValueToInt32) modify |
|
548 // temps after calling into the VM. This is fine because no other |
|
549 // instructions (including this OsiPoint) will depend on them. Also |
|
550 // backtracking can also use the same register for an input and an output. |
|
551 // These are marked as clobbered and shouldn't get checked. |
|
552 RegisterSet liveRegs = safepoint->liveRegs(); |
|
553 liveRegs = RegisterSet::Intersect(liveRegs, RegisterSet::Not(safepoint->clobberedRegs())); |
|
554 |
|
555 VerifyOp op(masm, &failure); |
|
556 HandleRegisterDump<VerifyOp>(op, masm, liveRegs, scratch, allRegs.getAny()); |
|
557 |
|
558 masm.jump(&done); |
|
559 |
|
560 // Do not profile the callWithABI that occurs below. This is to avoid a |
|
561 // rare corner case that occurs when profiling interacts with itself: |
|
562 // |
|
563 // When slow profiling assertions are turned on, FunctionBoundary ops |
|
564 // (which update the profiler pseudo-stack) may emit a callVM, which |
|
565 // forces them to have an osi point associated with them. The |
|
566 // FunctionBoundary for inline function entry is added to the caller's |
|
567 // graph with a PC from the caller's code, but during codegen it modifies |
|
568 // SPS instrumentation to add the callee as the current top-most script. |
|
569 // When codegen gets to the OSIPoint, and the callWithABI below is |
|
570 // emitted, the codegen thinks that the current frame is the callee, but |
|
571 // the PC it's using from the OSIPoint refers to the caller. This causes |
|
572 // the profiler instrumentation of the callWithABI below to ASSERT, since |
|
573 // the script and pc are mismatched. To avoid this, we simply omit |
|
574 // instrumentation for these callWithABIs. |
|
575 masm.bind(&failure); |
|
576 masm.setupUnalignedABICall(0, scratch); |
|
577 masm.callWithABINoProfiling(JS_FUNC_TO_DATA_PTR(void *, OsiPointRegisterCheckFailed)); |
|
578 masm.breakpoint(); |
|
579 |
|
580 masm.bind(&done); |
|
581 masm.pop(scratch); |
|
582 } |
|
583 |
|
584 bool |
|
585 CodeGeneratorShared::shouldVerifyOsiPointRegs(LSafepoint *safepoint) |
|
586 { |
|
587 if (!js_JitOptions.checkOsiPointRegisters) |
|
588 return false; |
|
589 |
|
590 if (gen->info().executionMode() != SequentialExecution) |
|
591 return false; |
|
592 |
|
593 if (safepoint->liveRegs().empty(true) && safepoint->liveRegs().empty(false)) |
|
594 return false; // No registers to check. |
|
595 |
|
596 return true; |
|
597 } |
|
598 |
|
599 void |
|
600 CodeGeneratorShared::resetOsiPointRegs(LSafepoint *safepoint) |
|
601 { |
|
602 if (!shouldVerifyOsiPointRegs(safepoint)) |
|
603 return; |
|
604 |
|
605 // Set checkRegs to 0. If we perform a VM call, the instruction |
|
606 // will set it to 1. |
|
607 GeneralRegisterSet allRegs(GeneralRegisterSet::All()); |
|
608 Register scratch = allRegs.takeAny(); |
|
609 masm.push(scratch); |
|
610 masm.loadJitActivation(scratch); |
|
611 Address checkRegs(scratch, JitActivation::offsetOfCheckRegs()); |
|
612 masm.store32(Imm32(0), checkRegs); |
|
613 masm.pop(scratch); |
|
614 } |
|
615 #endif |
|
616 |
|
617 // Before doing any call to Cpp, you should ensure that volatile |
|
618 // registers are evicted by the register allocator. |
|
619 bool |
|
620 CodeGeneratorShared::callVM(const VMFunction &fun, LInstruction *ins, const Register *dynStack) |
|
621 { |
|
622 // Different execution modes have different sets of VM functions. |
|
623 JS_ASSERT(fun.executionMode == gen->info().executionMode()); |
|
624 |
|
625 // If we're calling a function with an out parameter type of double, make |
|
626 // sure we have an FPU. |
|
627 JS_ASSERT_IF(fun.outParam == Type_Double, GetIonContext()->runtime->jitSupportsFloatingPoint()); |
|
628 |
|
629 #ifdef DEBUG |
|
630 if (ins->mirRaw()) { |
|
631 JS_ASSERT(ins->mirRaw()->isInstruction()); |
|
632 MInstruction *mir = ins->mirRaw()->toInstruction(); |
|
633 JS_ASSERT_IF(mir->isEffectful(), mir->resumePoint()); |
|
634 } |
|
635 #endif |
|
636 |
|
637 #ifdef JS_TRACE_LOGGING |
|
638 if (!emitTracelogStartEvent(TraceLogger::VM)) |
|
639 return false; |
|
640 #endif |
|
641 |
|
642 // Stack is: |
|
643 // ... frame ... |
|
644 // [args] |
|
645 #ifdef DEBUG |
|
646 JS_ASSERT(pushedArgs_ == fun.explicitArgs); |
|
647 pushedArgs_ = 0; |
|
648 #endif |
|
649 |
|
650 // Get the wrapper of the VM function. |
|
651 JitCode *wrapper = gen->jitRuntime()->getVMWrapper(fun); |
|
652 if (!wrapper) |
|
653 return false; |
|
654 |
|
655 #ifdef CHECK_OSIPOINT_REGISTERS |
|
656 if (shouldVerifyOsiPointRegs(ins->safepoint())) |
|
657 StoreAllLiveRegs(masm, ins->safepoint()->liveRegs()); |
|
658 #endif |
|
659 |
|
660 // Call the wrapper function. The wrapper is in charge to unwind the stack |
|
661 // when returning from the call. Failures are handled with exceptions based |
|
662 // on the return value of the C functions. To guard the outcome of the |
|
663 // returned value, use another LIR instruction. |
|
664 uint32_t callOffset; |
|
665 if (dynStack) |
|
666 callOffset = masm.callWithExitFrame(wrapper, *dynStack); |
|
667 else |
|
668 callOffset = masm.callWithExitFrame(wrapper); |
|
669 |
|
670 if (!markSafepointAt(callOffset, ins)) |
|
671 return false; |
|
672 |
|
673 // Remove rest of the frame left on the stack. We remove the return address |
|
674 // which is implicitly poped when returning. |
|
675 int framePop = sizeof(IonExitFrameLayout) - sizeof(void*); |
|
676 |
|
677 // Pop arguments from framePushed. |
|
678 masm.implicitPop(fun.explicitStackSlots() * sizeof(void *) + framePop); |
|
679 // Stack is: |
|
680 // ... frame ... |
|
681 |
|
682 #ifdef JS_TRACE_LOGGING |
|
683 if (!emitTracelogStopEvent(TraceLogger::VM)) |
|
684 return false; |
|
685 #endif |
|
686 |
|
687 return true; |
|
688 } |
|
689 |
|
690 class OutOfLineTruncateSlow : public OutOfLineCodeBase<CodeGeneratorShared> |
|
691 { |
|
692 FloatRegister src_; |
|
693 Register dest_; |
|
694 bool needFloat32Conversion_; |
|
695 |
|
696 public: |
|
697 OutOfLineTruncateSlow(FloatRegister src, Register dest, bool needFloat32Conversion = false) |
|
698 : src_(src), dest_(dest), needFloat32Conversion_(needFloat32Conversion) |
|
699 { } |
|
700 |
|
701 bool accept(CodeGeneratorShared *codegen) { |
|
702 return codegen->visitOutOfLineTruncateSlow(this); |
|
703 } |
|
704 FloatRegister src() const { |
|
705 return src_; |
|
706 } |
|
707 Register dest() const { |
|
708 return dest_; |
|
709 } |
|
710 bool needFloat32Conversion() const { |
|
711 return needFloat32Conversion_; |
|
712 } |
|
713 |
|
714 }; |
|
715 |
|
716 OutOfLineCode * |
|
717 CodeGeneratorShared::oolTruncateDouble(const FloatRegister &src, const Register &dest) |
|
718 { |
|
719 OutOfLineTruncateSlow *ool = new(alloc()) OutOfLineTruncateSlow(src, dest); |
|
720 if (!addOutOfLineCode(ool)) |
|
721 return nullptr; |
|
722 return ool; |
|
723 } |
|
724 |
|
725 bool |
|
726 CodeGeneratorShared::emitTruncateDouble(const FloatRegister &src, const Register &dest) |
|
727 { |
|
728 OutOfLineCode *ool = oolTruncateDouble(src, dest); |
|
729 if (!ool) |
|
730 return false; |
|
731 |
|
732 masm.branchTruncateDouble(src, dest, ool->entry()); |
|
733 masm.bind(ool->rejoin()); |
|
734 return true; |
|
735 } |
|
736 |
|
737 bool |
|
738 CodeGeneratorShared::emitTruncateFloat32(const FloatRegister &src, const Register &dest) |
|
739 { |
|
740 OutOfLineTruncateSlow *ool = new(alloc()) OutOfLineTruncateSlow(src, dest, true); |
|
741 if (!addOutOfLineCode(ool)) |
|
742 return false; |
|
743 |
|
744 masm.branchTruncateFloat32(src, dest, ool->entry()); |
|
745 masm.bind(ool->rejoin()); |
|
746 return true; |
|
747 } |
|
748 |
|
749 bool |
|
750 CodeGeneratorShared::visitOutOfLineTruncateSlow(OutOfLineTruncateSlow *ool) |
|
751 { |
|
752 FloatRegister src = ool->src(); |
|
753 Register dest = ool->dest(); |
|
754 |
|
755 saveVolatile(dest); |
|
756 |
|
757 if (ool->needFloat32Conversion()) { |
|
758 masm.push(src); |
|
759 masm.convertFloat32ToDouble(src, src); |
|
760 } |
|
761 |
|
762 masm.setupUnalignedABICall(1, dest); |
|
763 masm.passABIArg(src, MoveOp::DOUBLE); |
|
764 if (gen->compilingAsmJS()) |
|
765 masm.callWithABI(AsmJSImm_ToInt32); |
|
766 else |
|
767 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, js::ToInt32)); |
|
768 masm.storeCallResult(dest); |
|
769 |
|
770 if (ool->needFloat32Conversion()) |
|
771 masm.pop(src); |
|
772 |
|
773 restoreVolatile(dest); |
|
774 |
|
775 masm.jump(ool->rejoin()); |
|
776 return true; |
|
777 } |
|
778 |
|
779 bool |
|
780 CodeGeneratorShared::omitOverRecursedCheck() const |
|
781 { |
|
782 // If the current function makes no calls (which means it isn't recursive) |
|
783 // and it uses only a small amount of stack space, it doesn't need a |
|
784 // stack overflow check. Note that the actual number here is somewhat |
|
785 // arbitrary, and codegen actually uses small bounded amounts of |
|
786 // additional stack space in some cases too. |
|
787 return frameSize() < 64 && !gen->performsCall(); |
|
788 } |
|
789 |
|
790 void |
|
791 CodeGeneratorShared::emitPreBarrier(Register base, const LAllocation *index, MIRType type) |
|
792 { |
|
793 if (index->isConstant()) { |
|
794 Address address(base, ToInt32(index) * sizeof(Value)); |
|
795 masm.patchableCallPreBarrier(address, type); |
|
796 } else { |
|
797 BaseIndex address(base, ToRegister(index), TimesEight); |
|
798 masm.patchableCallPreBarrier(address, type); |
|
799 } |
|
800 } |
|
801 |
|
802 void |
|
803 CodeGeneratorShared::emitPreBarrier(Address address, MIRType type) |
|
804 { |
|
805 masm.patchableCallPreBarrier(address, type); |
|
806 } |
|
807 |
|
808 void |
|
809 CodeGeneratorShared::dropArguments(unsigned argc) |
|
810 { |
|
811 pushedArgumentSlots_.shrinkBy(argc); |
|
812 } |
|
813 |
|
814 bool |
|
815 CodeGeneratorShared::markArgumentSlots(LSafepoint *safepoint) |
|
816 { |
|
817 for (size_t i = 0; i < pushedArgumentSlots_.length(); i++) { |
|
818 if (!safepoint->addValueSlot(pushedArgumentSlots_[i])) |
|
819 return false; |
|
820 } |
|
821 return true; |
|
822 } |
|
823 |
|
824 OutOfLineAbortPar * |
|
825 CodeGeneratorShared::oolAbortPar(ParallelBailoutCause cause, MBasicBlock *basicBlock, |
|
826 jsbytecode *bytecode) |
|
827 { |
|
828 OutOfLineAbortPar *ool = new(alloc()) OutOfLineAbortPar(cause, basicBlock, bytecode); |
|
829 if (!ool || !addOutOfLineCode(ool)) |
|
830 return nullptr; |
|
831 return ool; |
|
832 } |
|
833 |
|
834 OutOfLineAbortPar * |
|
835 CodeGeneratorShared::oolAbortPar(ParallelBailoutCause cause, LInstruction *lir) |
|
836 { |
|
837 MDefinition *mir = lir->mirRaw(); |
|
838 MBasicBlock *block = mir->block(); |
|
839 jsbytecode *pc = mir->trackedPc(); |
|
840 if (!pc) { |
|
841 if (lir->snapshot()) |
|
842 pc = lir->snapshot()->mir()->pc(); |
|
843 else |
|
844 pc = block->pc(); |
|
845 } |
|
846 return oolAbortPar(cause, block, pc); |
|
847 } |
|
848 |
|
849 OutOfLinePropagateAbortPar * |
|
850 CodeGeneratorShared::oolPropagateAbortPar(LInstruction *lir) |
|
851 { |
|
852 OutOfLinePropagateAbortPar *ool = new(alloc()) OutOfLinePropagateAbortPar(lir); |
|
853 if (!ool || !addOutOfLineCode(ool)) |
|
854 return nullptr; |
|
855 return ool; |
|
856 } |
|
857 |
|
858 bool |
|
859 OutOfLineAbortPar::generate(CodeGeneratorShared *codegen) |
|
860 { |
|
861 codegen->callTraceLIR(0xDEADBEEF, nullptr, "AbortPar"); |
|
862 return codegen->visitOutOfLineAbortPar(this); |
|
863 } |
|
864 |
|
865 bool |
|
866 OutOfLinePropagateAbortPar::generate(CodeGeneratorShared *codegen) |
|
867 { |
|
868 codegen->callTraceLIR(0xDEADBEEF, nullptr, "AbortPar"); |
|
869 return codegen->visitOutOfLinePropagateAbortPar(this); |
|
870 } |
|
871 |
|
872 bool |
|
873 CodeGeneratorShared::callTraceLIR(uint32_t blockIndex, LInstruction *lir, |
|
874 const char *bailoutName) |
|
875 { |
|
876 JS_ASSERT_IF(!lir, bailoutName); |
|
877 |
|
878 if (!IonSpewEnabled(IonSpew_Trace)) |
|
879 return true; |
|
880 |
|
881 uint32_t execMode = (uint32_t) gen->info().executionMode(); |
|
882 uint32_t lirIndex; |
|
883 const char *lirOpName; |
|
884 const char *mirOpName; |
|
885 JSScript *script; |
|
886 jsbytecode *pc; |
|
887 |
|
888 masm.PushRegsInMask(RegisterSet::Volatile()); |
|
889 masm.reserveStack(sizeof(IonLIRTraceData)); |
|
890 |
|
891 // This first move is here so that when you scan the disassembly, |
|
892 // you can easily pick out where each instruction begins. The |
|
893 // next few items indicate to you the Basic Block / LIR. |
|
894 masm.move32(Imm32(0xDEADBEEF), CallTempReg0); |
|
895 |
|
896 if (lir) { |
|
897 lirIndex = lir->id(); |
|
898 lirOpName = lir->opName(); |
|
899 if (MDefinition *mir = lir->mirRaw()) { |
|
900 mirOpName = mir->opName(); |
|
901 script = mir->block()->info().script(); |
|
902 pc = mir->trackedPc(); |
|
903 } else { |
|
904 mirOpName = nullptr; |
|
905 script = nullptr; |
|
906 pc = nullptr; |
|
907 } |
|
908 } else { |
|
909 blockIndex = lirIndex = 0xDEADBEEF; |
|
910 lirOpName = mirOpName = bailoutName; |
|
911 script = nullptr; |
|
912 pc = nullptr; |
|
913 } |
|
914 |
|
915 masm.store32(Imm32(blockIndex), |
|
916 Address(StackPointer, offsetof(IonLIRTraceData, blockIndex))); |
|
917 masm.store32(Imm32(lirIndex), |
|
918 Address(StackPointer, offsetof(IonLIRTraceData, lirIndex))); |
|
919 masm.store32(Imm32(execMode), |
|
920 Address(StackPointer, offsetof(IonLIRTraceData, execModeInt))); |
|
921 masm.storePtr(ImmPtr(lirOpName), |
|
922 Address(StackPointer, offsetof(IonLIRTraceData, lirOpName))); |
|
923 masm.storePtr(ImmPtr(mirOpName), |
|
924 Address(StackPointer, offsetof(IonLIRTraceData, mirOpName))); |
|
925 masm.storePtr(ImmGCPtr(script), |
|
926 Address(StackPointer, offsetof(IonLIRTraceData, script))); |
|
927 masm.storePtr(ImmPtr(pc), |
|
928 Address(StackPointer, offsetof(IonLIRTraceData, pc))); |
|
929 |
|
930 masm.movePtr(StackPointer, CallTempReg0); |
|
931 masm.setupUnalignedABICall(1, CallTempReg1); |
|
932 masm.passABIArg(CallTempReg0); |
|
933 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, TraceLIR)); |
|
934 |
|
935 masm.freeStack(sizeof(IonLIRTraceData)); |
|
936 masm.PopRegsInMask(RegisterSet::Volatile()); |
|
937 |
|
938 return true; |
|
939 } |
|
940 |
|
941 typedef bool (*InterruptCheckFn)(JSContext *); |
|
942 const VMFunction InterruptCheckInfo = FunctionInfo<InterruptCheckFn>(InterruptCheck); |
|
943 |
|
944 Label * |
|
945 CodeGeneratorShared::labelForBackedgeWithImplicitCheck(MBasicBlock *mir) |
|
946 { |
|
947 // If this is a loop backedge to a loop header with an implicit interrupt |
|
948 // check, use a patchable jump. Skip this search if compiling without a |
|
949 // script for asm.js, as there will be no interrupt check instruction. |
|
950 // Due to critical edge unsplitting there may no longer be unique loop |
|
951 // backedges, so just look for any edge going to an earlier block in RPO. |
|
952 if (!gen->compilingAsmJS() && mir->isLoopHeader() && mir->id() <= current->mir()->id()) { |
|
953 for (LInstructionIterator iter = mir->lir()->begin(); iter != mir->lir()->end(); iter++) { |
|
954 if (iter->isLabel() || iter->isMoveGroup()) { |
|
955 // Continue searching for an interrupt check. |
|
956 } else if (iter->isInterruptCheckImplicit()) { |
|
957 return iter->toInterruptCheckImplicit()->oolEntry(); |
|
958 } else { |
|
959 // The interrupt check should be the first instruction in the |
|
960 // loop header other than the initial label and move groups. |
|
961 JS_ASSERT(iter->isInterruptCheck() || iter->isInterruptCheckPar()); |
|
962 return nullptr; |
|
963 } |
|
964 } |
|
965 } |
|
966 |
|
967 return nullptr; |
|
968 } |
|
969 |
|
970 void |
|
971 CodeGeneratorShared::jumpToBlock(MBasicBlock *mir) |
|
972 { |
|
973 // No jump necessary if we can fall through to the next block. |
|
974 if (isNextBlock(mir->lir())) |
|
975 return; |
|
976 |
|
977 if (Label *oolEntry = labelForBackedgeWithImplicitCheck(mir)) { |
|
978 // Note: the backedge is initially a jump to the next instruction. |
|
979 // It will be patched to the target block's label during link(). |
|
980 RepatchLabel rejoin; |
|
981 CodeOffsetJump backedge = masm.jumpWithPatch(&rejoin); |
|
982 masm.bind(&rejoin); |
|
983 |
|
984 masm.propagateOOM(patchableBackedges_.append(PatchableBackedgeInfo(backedge, mir->lir()->label(), oolEntry))); |
|
985 } else { |
|
986 masm.jump(mir->lir()->label()); |
|
987 } |
|
988 } |
|
989 |
|
990 // This function is not used for MIPS. MIPS has branchToBlock. |
|
991 #ifndef JS_CODEGEN_MIPS |
|
992 void |
|
993 CodeGeneratorShared::jumpToBlock(MBasicBlock *mir, Assembler::Condition cond) |
|
994 { |
|
995 if (Label *oolEntry = labelForBackedgeWithImplicitCheck(mir)) { |
|
996 // Note: the backedge is initially a jump to the next instruction. |
|
997 // It will be patched to the target block's label during link(). |
|
998 RepatchLabel rejoin; |
|
999 CodeOffsetJump backedge = masm.jumpWithPatch(&rejoin, cond); |
|
1000 masm.bind(&rejoin); |
|
1001 |
|
1002 masm.propagateOOM(patchableBackedges_.append(PatchableBackedgeInfo(backedge, mir->lir()->label(), oolEntry))); |
|
1003 } else { |
|
1004 masm.j(cond, mir->lir()->label()); |
|
1005 } |
|
1006 } |
|
1007 #endif |
|
1008 |
|
1009 size_t |
|
1010 CodeGeneratorShared::addCacheLocations(const CacheLocationList &locs, size_t *numLocs) |
|
1011 { |
|
1012 size_t firstIndex = runtimeData_.length(); |
|
1013 size_t numLocations = 0; |
|
1014 for (CacheLocationList::iterator iter = locs.begin(); iter != locs.end(); iter++) { |
|
1015 // allocateData() ensures that sizeof(CacheLocation) is word-aligned. |
|
1016 // If this changes, we will need to pad to ensure alignment. |
|
1017 size_t curIndex = allocateData(sizeof(CacheLocation)); |
|
1018 new (&runtimeData_[curIndex]) CacheLocation(iter->pc, iter->script); |
|
1019 numLocations++; |
|
1020 } |
|
1021 JS_ASSERT(numLocations != 0); |
|
1022 *numLocs = numLocations; |
|
1023 return firstIndex; |
|
1024 } |
|
1025 |
|
1026 ReciprocalMulConstants |
|
1027 CodeGeneratorShared::computeDivisionConstants(int d) { |
|
1028 // In what follows, d is positive and is not a power of 2. |
|
1029 JS_ASSERT(d > 0 && (d & (d - 1)) != 0); |
|
1030 |
|
1031 // Speeding up division by non power-of-2 constants is possible by |
|
1032 // calculating, during compilation, a value M such that high-order |
|
1033 // bits of M*n correspond to the result of the division. Formally, |
|
1034 // we compute values 0 <= M < 2^32 and 0 <= s < 31 such that |
|
1035 // (M * n) >> (32 + s) = floor(n/d) if n >= 0 |
|
1036 // (M * n) >> (32 + s) = ceil(n/d) - 1 if n < 0. |
|
1037 // The original presentation of this technique appears in Hacker's |
|
1038 // Delight, a book by Henry S. Warren, Jr.. A proof of correctness |
|
1039 // for our version follows. |
|
1040 |
|
1041 // Define p = 32 + s, M = ceil(2^p/d), and assume that s satisfies |
|
1042 // M - 2^p/d <= 2^(s+1)/d. (1) |
|
1043 // (Observe that s = FloorLog32(d) satisfies this, because in this |
|
1044 // case d <= 2^(s+1) and so the RHS of (1) is at least one). Then, |
|
1045 // |
|
1046 // a) If s <= FloorLog32(d), then M <= 2^32 - 1. |
|
1047 // Proof: Indeed, M is monotone in s and, for s = FloorLog32(d), |
|
1048 // the inequalities 2^31 > d >= 2^s + 1 readily imply |
|
1049 // 2^p / d = 2^p/(d - 1) * (d - 1)/d |
|
1050 // <= 2^32 * (1 - 1/d) < 2 * (2^31 - 1) = 2^32 - 2. |
|
1051 // The claim follows by applying the ceiling function. |
|
1052 // |
|
1053 // b) For any 0 <= n < 2^31, floor(Mn/2^p) = floor(n/d). |
|
1054 // Proof: Put x = floor(Mn/2^p); it's the unique integer for which |
|
1055 // Mn/2^p - 1 < x <= Mn/2^p. (2) |
|
1056 // Using M >= 2^p/d on the LHS and (1) on the RHS, we get |
|
1057 // n/d - 1 < x <= n/d + n/(2^31 d) < n/d + 1/d. |
|
1058 // Since x is an integer, it's not in the interval (n/d, (n+1)/d), |
|
1059 // and so n/d - 1 < x <= n/d, which implies x = floor(n/d). |
|
1060 // |
|
1061 // c) For any -2^31 <= n < 0, floor(Mn/2^p) + 1 = ceil(n/d). |
|
1062 // Proof: The proof is similar. Equation (2) holds as above. Using |
|
1063 // M > 2^p/d (d isn't a power of 2) on the RHS and (1) on the LHS, |
|
1064 // n/d + n/(2^31 d) - 1 < x < n/d. |
|
1065 // Using n >= -2^31 and summing 1, |
|
1066 // n/d - 1/d < x + 1 < n/d + 1. |
|
1067 // Since x + 1 is an integer, this implies n/d <= x + 1 < n/d + 1. |
|
1068 // In other words, x + 1 = ceil(n/d). |
|
1069 // |
|
1070 // Condition (1) isn't necessary for the existence of M and s with |
|
1071 // the properties above. Hacker's Delight provides a slightly less |
|
1072 // restrictive condition when d >= 196611, at the cost of a 3-page |
|
1073 // proof of correctness. |
|
1074 |
|
1075 // Note that, since d*M - 2^p = d - (2^p)%d, (1) can be written as |
|
1076 // 2^(s+1) >= d - (2^p)%d. |
|
1077 // We now compute the least s with this property... |
|
1078 |
|
1079 int32_t shift = 0; |
|
1080 while ((int64_t(1) << (shift+1)) + (int64_t(1) << (shift+32)) % d < d) |
|
1081 shift++; |
|
1082 |
|
1083 // ...and the corresponding M. This may not fit in a signed 32-bit |
|
1084 // integer; we will compute (M - 2^32) * n + (2^32 * n) instead of |
|
1085 // M * n if this is the case (cf. item (a) above). |
|
1086 ReciprocalMulConstants rmc; |
|
1087 rmc.multiplier = int32_t((int64_t(1) << (shift+32))/d + 1); |
|
1088 rmc.shiftAmount = shift; |
|
1089 |
|
1090 return rmc; |
|
1091 } |
|
1092 |
|
1093 |
|
1094 #ifdef JS_TRACE_LOGGING |
|
1095 |
|
1096 bool |
|
1097 CodeGeneratorShared::emitTracelogScript(bool isStart) |
|
1098 { |
|
1099 RegisterSet regs = RegisterSet::Volatile(); |
|
1100 Register logger = regs.takeGeneral(); |
|
1101 Register script = regs.takeGeneral(); |
|
1102 |
|
1103 masm.Push(logger); |
|
1104 masm.Push(script); |
|
1105 |
|
1106 CodeOffsetLabel patchLogger = masm.movWithPatch(ImmPtr(nullptr), logger); |
|
1107 if (!patchableTraceLoggers_.append(patchLogger)) |
|
1108 return false; |
|
1109 |
|
1110 CodeOffsetLabel patchScript = masm.movWithPatch(ImmWord(0), script); |
|
1111 if (!patchableTLScripts_.append(patchScript)) |
|
1112 return false; |
|
1113 |
|
1114 if (isStart) |
|
1115 masm.tracelogStart(logger, script); |
|
1116 else |
|
1117 masm.tracelogStop(logger, script); |
|
1118 |
|
1119 masm.Pop(script); |
|
1120 masm.Pop(logger); |
|
1121 return true; |
|
1122 } |
|
1123 |
|
1124 bool |
|
1125 CodeGeneratorShared::emitTracelogTree(bool isStart, uint32_t textId) |
|
1126 { |
|
1127 if (!TraceLogTextIdEnabled(textId)) |
|
1128 return true; |
|
1129 |
|
1130 RegisterSet regs = RegisterSet::Volatile(); |
|
1131 Register logger = regs.takeGeneral(); |
|
1132 |
|
1133 masm.Push(logger); |
|
1134 |
|
1135 CodeOffsetLabel patchLocation = masm.movWithPatch(ImmPtr(nullptr), logger); |
|
1136 if (!patchableTraceLoggers_.append(patchLocation)) |
|
1137 return false; |
|
1138 |
|
1139 if (isStart) { |
|
1140 masm.tracelogStart(logger, textId); |
|
1141 } else { |
|
1142 #ifdef DEBUG |
|
1143 masm.tracelogStop(logger, textId); |
|
1144 #else |
|
1145 masm.tracelogStop(logger); |
|
1146 #endif |
|
1147 } |
|
1148 |
|
1149 masm.Pop(logger); |
|
1150 return true; |
|
1151 } |
|
1152 #endif |
|
1153 |
|
1154 } // namespace jit |
|
1155 } // namespace js |