|
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-x86-shared.h" |
|
8 |
|
9 #include "mozilla/DebugOnly.h" |
|
10 #include "mozilla/MathAlgorithms.h" |
|
11 |
|
12 #include "jsmath.h" |
|
13 |
|
14 #include "jit/IonFrames.h" |
|
15 #include "jit/IonLinker.h" |
|
16 #include "jit/JitCompartment.h" |
|
17 #include "jit/RangeAnalysis.h" |
|
18 #include "vm/TraceLogging.h" |
|
19 |
|
20 #include "jit/shared/CodeGenerator-shared-inl.h" |
|
21 |
|
22 using namespace js; |
|
23 using namespace js::jit; |
|
24 |
|
25 using mozilla::Abs; |
|
26 using mozilla::FloatingPoint; |
|
27 using mozilla::FloorLog2; |
|
28 using mozilla::NegativeInfinity; |
|
29 using mozilla::SpecificNaN; |
|
30 |
|
31 namespace js { |
|
32 namespace jit { |
|
33 |
|
34 CodeGeneratorX86Shared::CodeGeneratorX86Shared(MIRGenerator *gen, LIRGraph *graph, MacroAssembler *masm) |
|
35 : CodeGeneratorShared(gen, graph, masm) |
|
36 { |
|
37 } |
|
38 |
|
39 bool |
|
40 CodeGeneratorX86Shared::generatePrologue() |
|
41 { |
|
42 JS_ASSERT(!gen->compilingAsmJS()); |
|
43 |
|
44 // Note that this automatically sets MacroAssembler::framePushed(). |
|
45 masm.reserveStack(frameSize()); |
|
46 |
|
47 return true; |
|
48 } |
|
49 |
|
50 bool |
|
51 CodeGeneratorX86Shared::generateAsmJSPrologue(Label *stackOverflowLabel) |
|
52 { |
|
53 JS_ASSERT(gen->compilingAsmJS()); |
|
54 |
|
55 // The asm.js over-recursed handler wants to be able to assume that SP |
|
56 // points to the return address, so perform the check before pushing |
|
57 // frameDepth. |
|
58 if (!omitOverRecursedCheck()) { |
|
59 masm.branchPtr(Assembler::AboveOrEqual, |
|
60 AsmJSAbsoluteAddress(AsmJSImm_StackLimit), |
|
61 StackPointer, |
|
62 stackOverflowLabel); |
|
63 } |
|
64 |
|
65 // Note that this automatically sets MacroAssembler::framePushed(). |
|
66 masm.reserveStack(frameSize()); |
|
67 return true; |
|
68 } |
|
69 |
|
70 bool |
|
71 CodeGeneratorX86Shared::generateEpilogue() |
|
72 { |
|
73 masm.bind(&returnLabel_); |
|
74 |
|
75 #ifdef JS_TRACE_LOGGING |
|
76 if (!gen->compilingAsmJS() && gen->info().executionMode() == SequentialExecution) { |
|
77 if (!emitTracelogStopEvent(TraceLogger::IonMonkey)) |
|
78 return false; |
|
79 if (!emitTracelogScriptStop()) |
|
80 return false; |
|
81 } |
|
82 #endif |
|
83 |
|
84 // Pop the stack we allocated at the start of the function. |
|
85 masm.freeStack(frameSize()); |
|
86 JS_ASSERT(masm.framePushed() == 0); |
|
87 |
|
88 masm.ret(); |
|
89 return true; |
|
90 } |
|
91 |
|
92 bool |
|
93 OutOfLineBailout::accept(CodeGeneratorX86Shared *codegen) |
|
94 { |
|
95 return codegen->visitOutOfLineBailout(this); |
|
96 } |
|
97 |
|
98 void |
|
99 CodeGeneratorX86Shared::emitBranch(Assembler::Condition cond, MBasicBlock *mirTrue, |
|
100 MBasicBlock *mirFalse, Assembler::NaNCond ifNaN) |
|
101 { |
|
102 if (ifNaN == Assembler::NaN_IsFalse) |
|
103 jumpToBlock(mirFalse, Assembler::Parity); |
|
104 else if (ifNaN == Assembler::NaN_IsTrue) |
|
105 jumpToBlock(mirTrue, Assembler::Parity); |
|
106 |
|
107 if (isNextBlock(mirFalse->lir())) { |
|
108 jumpToBlock(mirTrue, cond); |
|
109 } else { |
|
110 jumpToBlock(mirFalse, Assembler::InvertCondition(cond)); |
|
111 jumpToBlock(mirTrue); |
|
112 } |
|
113 } |
|
114 |
|
115 bool |
|
116 CodeGeneratorX86Shared::visitDouble(LDouble *ins) |
|
117 { |
|
118 const LDefinition *out = ins->getDef(0); |
|
119 masm.loadConstantDouble(ins->getDouble(), ToFloatRegister(out)); |
|
120 return true; |
|
121 } |
|
122 |
|
123 bool |
|
124 CodeGeneratorX86Shared::visitFloat32(LFloat32 *ins) |
|
125 { |
|
126 const LDefinition *out = ins->getDef(0); |
|
127 masm.loadConstantFloat32(ins->getFloat(), ToFloatRegister(out)); |
|
128 return true; |
|
129 } |
|
130 |
|
131 bool |
|
132 CodeGeneratorX86Shared::visitTestIAndBranch(LTestIAndBranch *test) |
|
133 { |
|
134 const LAllocation *opd = test->input(); |
|
135 |
|
136 // Test the operand |
|
137 masm.testl(ToRegister(opd), ToRegister(opd)); |
|
138 emitBranch(Assembler::NonZero, test->ifTrue(), test->ifFalse()); |
|
139 return true; |
|
140 } |
|
141 |
|
142 bool |
|
143 CodeGeneratorX86Shared::visitTestDAndBranch(LTestDAndBranch *test) |
|
144 { |
|
145 const LAllocation *opd = test->input(); |
|
146 |
|
147 // ucomisd flags: |
|
148 // Z P C |
|
149 // --------- |
|
150 // NaN 1 1 1 |
|
151 // > 0 0 0 |
|
152 // < 0 0 1 |
|
153 // = 1 0 0 |
|
154 // |
|
155 // NaN is falsey, so comparing against 0 and then using the Z flag is |
|
156 // enough to determine which branch to take. |
|
157 masm.xorpd(ScratchFloatReg, ScratchFloatReg); |
|
158 masm.ucomisd(ToFloatRegister(opd), ScratchFloatReg); |
|
159 emitBranch(Assembler::NotEqual, test->ifTrue(), test->ifFalse()); |
|
160 return true; |
|
161 } |
|
162 |
|
163 bool |
|
164 CodeGeneratorX86Shared::visitTestFAndBranch(LTestFAndBranch *test) |
|
165 { |
|
166 const LAllocation *opd = test->input(); |
|
167 // ucomiss flags are the same as doubles; see comment above |
|
168 masm.xorps(ScratchFloatReg, ScratchFloatReg); |
|
169 masm.ucomiss(ToFloatRegister(opd), ScratchFloatReg); |
|
170 emitBranch(Assembler::NotEqual, test->ifTrue(), test->ifFalse()); |
|
171 return true; |
|
172 } |
|
173 |
|
174 bool |
|
175 CodeGeneratorX86Shared::visitBitAndAndBranch(LBitAndAndBranch *baab) |
|
176 { |
|
177 if (baab->right()->isConstant()) |
|
178 masm.testl(ToRegister(baab->left()), Imm32(ToInt32(baab->right()))); |
|
179 else |
|
180 masm.testl(ToRegister(baab->left()), ToRegister(baab->right())); |
|
181 emitBranch(Assembler::NonZero, baab->ifTrue(), baab->ifFalse()); |
|
182 return true; |
|
183 } |
|
184 |
|
185 void |
|
186 CodeGeneratorX86Shared::emitCompare(MCompare::CompareType type, const LAllocation *left, const LAllocation *right) |
|
187 { |
|
188 #ifdef JS_CODEGEN_X64 |
|
189 if (type == MCompare::Compare_Object) { |
|
190 masm.cmpq(ToRegister(left), ToOperand(right)); |
|
191 return; |
|
192 } |
|
193 #endif |
|
194 |
|
195 if (right->isConstant()) |
|
196 masm.cmpl(ToRegister(left), Imm32(ToInt32(right))); |
|
197 else |
|
198 masm.cmpl(ToRegister(left), ToOperand(right)); |
|
199 } |
|
200 |
|
201 bool |
|
202 CodeGeneratorX86Shared::visitCompare(LCompare *comp) |
|
203 { |
|
204 MCompare *mir = comp->mir(); |
|
205 emitCompare(mir->compareType(), comp->left(), comp->right()); |
|
206 masm.emitSet(JSOpToCondition(mir->compareType(), comp->jsop()), ToRegister(comp->output())); |
|
207 return true; |
|
208 } |
|
209 |
|
210 bool |
|
211 CodeGeneratorX86Shared::visitCompareAndBranch(LCompareAndBranch *comp) |
|
212 { |
|
213 MCompare *mir = comp->cmpMir(); |
|
214 emitCompare(mir->compareType(), comp->left(), comp->right()); |
|
215 Assembler::Condition cond = JSOpToCondition(mir->compareType(), comp->jsop()); |
|
216 emitBranch(cond, comp->ifTrue(), comp->ifFalse()); |
|
217 return true; |
|
218 } |
|
219 |
|
220 bool |
|
221 CodeGeneratorX86Shared::visitCompareD(LCompareD *comp) |
|
222 { |
|
223 FloatRegister lhs = ToFloatRegister(comp->left()); |
|
224 FloatRegister rhs = ToFloatRegister(comp->right()); |
|
225 |
|
226 Assembler::DoubleCondition cond = JSOpToDoubleCondition(comp->mir()->jsop()); |
|
227 |
|
228 Assembler::NaNCond nanCond = Assembler::NaNCondFromDoubleCondition(cond); |
|
229 if (comp->mir()->operandsAreNeverNaN()) |
|
230 nanCond = Assembler::NaN_HandledByCond; |
|
231 |
|
232 masm.compareDouble(cond, lhs, rhs); |
|
233 masm.emitSet(Assembler::ConditionFromDoubleCondition(cond), ToRegister(comp->output()), nanCond); |
|
234 return true; |
|
235 } |
|
236 |
|
237 bool |
|
238 CodeGeneratorX86Shared::visitCompareF(LCompareF *comp) |
|
239 { |
|
240 FloatRegister lhs = ToFloatRegister(comp->left()); |
|
241 FloatRegister rhs = ToFloatRegister(comp->right()); |
|
242 |
|
243 Assembler::DoubleCondition cond = JSOpToDoubleCondition(comp->mir()->jsop()); |
|
244 |
|
245 Assembler::NaNCond nanCond = Assembler::NaNCondFromDoubleCondition(cond); |
|
246 if (comp->mir()->operandsAreNeverNaN()) |
|
247 nanCond = Assembler::NaN_HandledByCond; |
|
248 |
|
249 masm.compareFloat(cond, lhs, rhs); |
|
250 masm.emitSet(Assembler::ConditionFromDoubleCondition(cond), ToRegister(comp->output()), nanCond); |
|
251 return true; |
|
252 } |
|
253 |
|
254 bool |
|
255 CodeGeneratorX86Shared::visitNotI(LNotI *ins) |
|
256 { |
|
257 masm.cmpl(ToRegister(ins->input()), Imm32(0)); |
|
258 masm.emitSet(Assembler::Equal, ToRegister(ins->output())); |
|
259 return true; |
|
260 } |
|
261 |
|
262 bool |
|
263 CodeGeneratorX86Shared::visitNotD(LNotD *ins) |
|
264 { |
|
265 FloatRegister opd = ToFloatRegister(ins->input()); |
|
266 |
|
267 // Not returns true if the input is a NaN. We don't have to worry about |
|
268 // it if we know the input is never NaN though. |
|
269 Assembler::NaNCond nanCond = Assembler::NaN_IsTrue; |
|
270 if (ins->mir()->operandIsNeverNaN()) |
|
271 nanCond = Assembler::NaN_HandledByCond; |
|
272 |
|
273 masm.xorpd(ScratchFloatReg, ScratchFloatReg); |
|
274 masm.compareDouble(Assembler::DoubleEqualOrUnordered, opd, ScratchFloatReg); |
|
275 masm.emitSet(Assembler::Equal, ToRegister(ins->output()), nanCond); |
|
276 return true; |
|
277 } |
|
278 |
|
279 bool |
|
280 CodeGeneratorX86Shared::visitNotF(LNotF *ins) |
|
281 { |
|
282 FloatRegister opd = ToFloatRegister(ins->input()); |
|
283 |
|
284 // Not returns true if the input is a NaN. We don't have to worry about |
|
285 // it if we know the input is never NaN though. |
|
286 Assembler::NaNCond nanCond = Assembler::NaN_IsTrue; |
|
287 if (ins->mir()->operandIsNeverNaN()) |
|
288 nanCond = Assembler::NaN_HandledByCond; |
|
289 |
|
290 masm.xorps(ScratchFloatReg, ScratchFloatReg); |
|
291 masm.compareFloat(Assembler::DoubleEqualOrUnordered, opd, ScratchFloatReg); |
|
292 masm.emitSet(Assembler::Equal, ToRegister(ins->output()), nanCond); |
|
293 return true; |
|
294 } |
|
295 |
|
296 bool |
|
297 CodeGeneratorX86Shared::visitCompareDAndBranch(LCompareDAndBranch *comp) |
|
298 { |
|
299 FloatRegister lhs = ToFloatRegister(comp->left()); |
|
300 FloatRegister rhs = ToFloatRegister(comp->right()); |
|
301 |
|
302 Assembler::DoubleCondition cond = JSOpToDoubleCondition(comp->cmpMir()->jsop()); |
|
303 |
|
304 Assembler::NaNCond nanCond = Assembler::NaNCondFromDoubleCondition(cond); |
|
305 if (comp->cmpMir()->operandsAreNeverNaN()) |
|
306 nanCond = Assembler::NaN_HandledByCond; |
|
307 |
|
308 masm.compareDouble(cond, lhs, rhs); |
|
309 emitBranch(Assembler::ConditionFromDoubleCondition(cond), comp->ifTrue(), comp->ifFalse(), nanCond); |
|
310 return true; |
|
311 } |
|
312 |
|
313 bool |
|
314 CodeGeneratorX86Shared::visitCompareFAndBranch(LCompareFAndBranch *comp) |
|
315 { |
|
316 FloatRegister lhs = ToFloatRegister(comp->left()); |
|
317 FloatRegister rhs = ToFloatRegister(comp->right()); |
|
318 |
|
319 Assembler::DoubleCondition cond = JSOpToDoubleCondition(comp->cmpMir()->jsop()); |
|
320 |
|
321 Assembler::NaNCond nanCond = Assembler::NaNCondFromDoubleCondition(cond); |
|
322 if (comp->cmpMir()->operandsAreNeverNaN()) |
|
323 nanCond = Assembler::NaN_HandledByCond; |
|
324 |
|
325 masm.compareFloat(cond, lhs, rhs); |
|
326 emitBranch(Assembler::ConditionFromDoubleCondition(cond), comp->ifTrue(), comp->ifFalse(), nanCond); |
|
327 return true; |
|
328 } |
|
329 |
|
330 bool |
|
331 CodeGeneratorX86Shared::visitAsmJSPassStackArg(LAsmJSPassStackArg *ins) |
|
332 { |
|
333 const MAsmJSPassStackArg *mir = ins->mir(); |
|
334 Address dst(StackPointer, mir->spOffset()); |
|
335 if (ins->arg()->isConstant()) { |
|
336 masm.storePtr(ImmWord(ToInt32(ins->arg())), dst); |
|
337 } else { |
|
338 if (ins->arg()->isGeneralReg()) |
|
339 masm.storePtr(ToRegister(ins->arg()), dst); |
|
340 else |
|
341 masm.storeDouble(ToFloatRegister(ins->arg()), dst); |
|
342 } |
|
343 return true; |
|
344 } |
|
345 |
|
346 bool |
|
347 CodeGeneratorX86Shared::generateOutOfLineCode() |
|
348 { |
|
349 if (!CodeGeneratorShared::generateOutOfLineCode()) |
|
350 return false; |
|
351 |
|
352 if (deoptLabel_.used()) { |
|
353 // All non-table-based bailouts will go here. |
|
354 masm.bind(&deoptLabel_); |
|
355 |
|
356 // Push the frame size, so the handler can recover the IonScript. |
|
357 masm.push(Imm32(frameSize())); |
|
358 |
|
359 JitCode *handler = gen->jitRuntime()->getGenericBailoutHandler(); |
|
360 masm.jmp(ImmPtr(handler->raw()), Relocation::JITCODE); |
|
361 } |
|
362 |
|
363 return true; |
|
364 } |
|
365 |
|
366 class BailoutJump { |
|
367 Assembler::Condition cond_; |
|
368 |
|
369 public: |
|
370 BailoutJump(Assembler::Condition cond) : cond_(cond) |
|
371 { } |
|
372 #ifdef JS_CODEGEN_X86 |
|
373 void operator()(MacroAssembler &masm, uint8_t *code) const { |
|
374 masm.j(cond_, ImmPtr(code), Relocation::HARDCODED); |
|
375 } |
|
376 #endif |
|
377 void operator()(MacroAssembler &masm, Label *label) const { |
|
378 masm.j(cond_, label); |
|
379 } |
|
380 }; |
|
381 |
|
382 class BailoutLabel { |
|
383 Label *label_; |
|
384 |
|
385 public: |
|
386 BailoutLabel(Label *label) : label_(label) |
|
387 { } |
|
388 #ifdef JS_CODEGEN_X86 |
|
389 void operator()(MacroAssembler &masm, uint8_t *code) const { |
|
390 masm.retarget(label_, ImmPtr(code), Relocation::HARDCODED); |
|
391 } |
|
392 #endif |
|
393 void operator()(MacroAssembler &masm, Label *label) const { |
|
394 masm.retarget(label_, label); |
|
395 } |
|
396 }; |
|
397 |
|
398 template <typename T> bool |
|
399 CodeGeneratorX86Shared::bailout(const T &binder, LSnapshot *snapshot) |
|
400 { |
|
401 CompileInfo &info = snapshot->mir()->block()->info(); |
|
402 switch (info.executionMode()) { |
|
403 case ParallelExecution: { |
|
404 // in parallel mode, make no attempt to recover, just signal an error. |
|
405 OutOfLineAbortPar *ool = oolAbortPar(ParallelBailoutUnsupported, |
|
406 snapshot->mir()->block(), |
|
407 snapshot->mir()->pc()); |
|
408 binder(masm, ool->entry()); |
|
409 return true; |
|
410 } |
|
411 case SequentialExecution: |
|
412 break; |
|
413 default: |
|
414 MOZ_ASSUME_UNREACHABLE("No such execution mode"); |
|
415 } |
|
416 |
|
417 if (!encode(snapshot)) |
|
418 return false; |
|
419 |
|
420 // Though the assembler doesn't track all frame pushes, at least make sure |
|
421 // the known value makes sense. We can't use bailout tables if the stack |
|
422 // isn't properly aligned to the static frame size. |
|
423 JS_ASSERT_IF(frameClass_ != FrameSizeClass::None() && deoptTable_, |
|
424 frameClass_.frameSize() == masm.framePushed()); |
|
425 |
|
426 #ifdef JS_CODEGEN_X86 |
|
427 // On x64, bailout tables are pointless, because 16 extra bytes are |
|
428 // reserved per external jump, whereas it takes only 10 bytes to encode a |
|
429 // a non-table based bailout. |
|
430 if (assignBailoutId(snapshot)) { |
|
431 binder(masm, deoptTable_->raw() + snapshot->bailoutId() * BAILOUT_TABLE_ENTRY_SIZE); |
|
432 return true; |
|
433 } |
|
434 #endif |
|
435 |
|
436 // We could not use a jump table, either because all bailout IDs were |
|
437 // reserved, or a jump table is not optimal for this frame size or |
|
438 // platform. Whatever, we will generate a lazy bailout. |
|
439 OutOfLineBailout *ool = new(alloc()) OutOfLineBailout(snapshot); |
|
440 if (!addOutOfLineCode(ool)) |
|
441 return false; |
|
442 |
|
443 binder(masm, ool->entry()); |
|
444 return true; |
|
445 } |
|
446 |
|
447 bool |
|
448 CodeGeneratorX86Shared::bailoutIf(Assembler::Condition condition, LSnapshot *snapshot) |
|
449 { |
|
450 return bailout(BailoutJump(condition), snapshot); |
|
451 } |
|
452 |
|
453 bool |
|
454 CodeGeneratorX86Shared::bailoutIf(Assembler::DoubleCondition condition, LSnapshot *snapshot) |
|
455 { |
|
456 JS_ASSERT(Assembler::NaNCondFromDoubleCondition(condition) == Assembler::NaN_HandledByCond); |
|
457 return bailoutIf(Assembler::ConditionFromDoubleCondition(condition), snapshot); |
|
458 } |
|
459 |
|
460 bool |
|
461 CodeGeneratorX86Shared::bailoutFrom(Label *label, LSnapshot *snapshot) |
|
462 { |
|
463 JS_ASSERT(label->used() && !label->bound()); |
|
464 return bailout(BailoutLabel(label), snapshot); |
|
465 } |
|
466 |
|
467 bool |
|
468 CodeGeneratorX86Shared::bailout(LSnapshot *snapshot) |
|
469 { |
|
470 Label label; |
|
471 masm.jump(&label); |
|
472 return bailoutFrom(&label, snapshot); |
|
473 } |
|
474 |
|
475 bool |
|
476 CodeGeneratorX86Shared::visitOutOfLineBailout(OutOfLineBailout *ool) |
|
477 { |
|
478 masm.push(Imm32(ool->snapshot()->snapshotOffset())); |
|
479 masm.jmp(&deoptLabel_); |
|
480 return true; |
|
481 } |
|
482 |
|
483 bool |
|
484 CodeGeneratorX86Shared::visitMinMaxD(LMinMaxD *ins) |
|
485 { |
|
486 FloatRegister first = ToFloatRegister(ins->first()); |
|
487 FloatRegister second = ToFloatRegister(ins->second()); |
|
488 #ifdef DEBUG |
|
489 FloatRegister output = ToFloatRegister(ins->output()); |
|
490 JS_ASSERT(first == output); |
|
491 #endif |
|
492 |
|
493 Label done, nan, minMaxInst; |
|
494 |
|
495 // Do a ucomisd to catch equality and NaNs, which both require special |
|
496 // handling. If the operands are ordered and inequal, we branch straight to |
|
497 // the min/max instruction. If we wanted, we could also branch for less-than |
|
498 // or greater-than here instead of using min/max, however these conditions |
|
499 // will sometimes be hard on the branch predictor. |
|
500 masm.ucomisd(first, second); |
|
501 masm.j(Assembler::NotEqual, &minMaxInst); |
|
502 if (!ins->mir()->range() || ins->mir()->range()->canBeNaN()) |
|
503 masm.j(Assembler::Parity, &nan); |
|
504 |
|
505 // Ordered and equal. The operands are bit-identical unless they are zero |
|
506 // and negative zero. These instructions merge the sign bits in that |
|
507 // case, and are no-ops otherwise. |
|
508 if (ins->mir()->isMax()) |
|
509 masm.andpd(second, first); |
|
510 else |
|
511 masm.orpd(second, first); |
|
512 masm.jump(&done); |
|
513 |
|
514 // x86's min/max are not symmetric; if either operand is a NaN, they return |
|
515 // the read-only operand. We need to return a NaN if either operand is a |
|
516 // NaN, so we explicitly check for a NaN in the read-write operand. |
|
517 if (!ins->mir()->range() || ins->mir()->range()->canBeNaN()) { |
|
518 masm.bind(&nan); |
|
519 masm.ucomisd(first, first); |
|
520 masm.j(Assembler::Parity, &done); |
|
521 } |
|
522 |
|
523 // When the values are inequal, or second is NaN, x86's min and max will |
|
524 // return the value we need. |
|
525 masm.bind(&minMaxInst); |
|
526 if (ins->mir()->isMax()) |
|
527 masm.maxsd(second, first); |
|
528 else |
|
529 masm.minsd(second, first); |
|
530 |
|
531 masm.bind(&done); |
|
532 return true; |
|
533 } |
|
534 |
|
535 bool |
|
536 CodeGeneratorX86Shared::visitAbsD(LAbsD *ins) |
|
537 { |
|
538 FloatRegister input = ToFloatRegister(ins->input()); |
|
539 JS_ASSERT(input == ToFloatRegister(ins->output())); |
|
540 // Load a value which is all ones except for the sign bit. |
|
541 masm.loadConstantDouble(SpecificNaN<double>(0, FloatingPoint<double>::SignificandBits), |
|
542 ScratchFloatReg); |
|
543 masm.andpd(ScratchFloatReg, input); |
|
544 return true; |
|
545 } |
|
546 |
|
547 bool |
|
548 CodeGeneratorX86Shared::visitAbsF(LAbsF *ins) |
|
549 { |
|
550 FloatRegister input = ToFloatRegister(ins->input()); |
|
551 JS_ASSERT(input == ToFloatRegister(ins->output())); |
|
552 // Same trick as visitAbsD above. |
|
553 masm.loadConstantFloat32(SpecificNaN<float>(0, FloatingPoint<float>::SignificandBits), |
|
554 ScratchFloatReg); |
|
555 masm.andps(ScratchFloatReg, input); |
|
556 return true; |
|
557 } |
|
558 |
|
559 bool |
|
560 CodeGeneratorX86Shared::visitSqrtD(LSqrtD *ins) |
|
561 { |
|
562 FloatRegister input = ToFloatRegister(ins->input()); |
|
563 FloatRegister output = ToFloatRegister(ins->output()); |
|
564 masm.sqrtsd(input, output); |
|
565 return true; |
|
566 } |
|
567 |
|
568 bool |
|
569 CodeGeneratorX86Shared::visitSqrtF(LSqrtF *ins) |
|
570 { |
|
571 FloatRegister input = ToFloatRegister(ins->input()); |
|
572 FloatRegister output = ToFloatRegister(ins->output()); |
|
573 masm.sqrtss(input, output); |
|
574 return true; |
|
575 } |
|
576 |
|
577 bool |
|
578 CodeGeneratorX86Shared::visitPowHalfD(LPowHalfD *ins) |
|
579 { |
|
580 FloatRegister input = ToFloatRegister(ins->input()); |
|
581 JS_ASSERT(input == ToFloatRegister(ins->output())); |
|
582 |
|
583 Label done, sqrt; |
|
584 |
|
585 if (!ins->mir()->operandIsNeverNegativeInfinity()) { |
|
586 // Branch if not -Infinity. |
|
587 masm.loadConstantDouble(NegativeInfinity<double>(), ScratchFloatReg); |
|
588 |
|
589 Assembler::DoubleCondition cond = Assembler::DoubleNotEqualOrUnordered; |
|
590 if (ins->mir()->operandIsNeverNaN()) |
|
591 cond = Assembler::DoubleNotEqual; |
|
592 masm.branchDouble(cond, input, ScratchFloatReg, &sqrt); |
|
593 |
|
594 // Math.pow(-Infinity, 0.5) == Infinity. |
|
595 masm.xorpd(input, input); |
|
596 masm.subsd(ScratchFloatReg, input); |
|
597 masm.jump(&done); |
|
598 |
|
599 masm.bind(&sqrt); |
|
600 } |
|
601 |
|
602 if (!ins->mir()->operandIsNeverNegativeZero()) { |
|
603 // Math.pow(-0, 0.5) == 0 == Math.pow(0, 0.5). Adding 0 converts any -0 to 0. |
|
604 masm.xorpd(ScratchFloatReg, ScratchFloatReg); |
|
605 masm.addsd(ScratchFloatReg, input); |
|
606 } |
|
607 |
|
608 masm.sqrtsd(input, input); |
|
609 |
|
610 masm.bind(&done); |
|
611 return true; |
|
612 } |
|
613 |
|
614 class OutOfLineUndoALUOperation : public OutOfLineCodeBase<CodeGeneratorX86Shared> |
|
615 { |
|
616 LInstruction *ins_; |
|
617 |
|
618 public: |
|
619 OutOfLineUndoALUOperation(LInstruction *ins) |
|
620 : ins_(ins) |
|
621 { } |
|
622 |
|
623 virtual bool accept(CodeGeneratorX86Shared *codegen) { |
|
624 return codegen->visitOutOfLineUndoALUOperation(this); |
|
625 } |
|
626 LInstruction *ins() const { |
|
627 return ins_; |
|
628 } |
|
629 }; |
|
630 |
|
631 bool |
|
632 CodeGeneratorX86Shared::visitAddI(LAddI *ins) |
|
633 { |
|
634 if (ins->rhs()->isConstant()) |
|
635 masm.addl(Imm32(ToInt32(ins->rhs())), ToOperand(ins->lhs())); |
|
636 else |
|
637 masm.addl(ToOperand(ins->rhs()), ToRegister(ins->lhs())); |
|
638 |
|
639 if (ins->snapshot()) { |
|
640 if (ins->recoversInput()) { |
|
641 OutOfLineUndoALUOperation *ool = new(alloc()) OutOfLineUndoALUOperation(ins); |
|
642 if (!addOutOfLineCode(ool)) |
|
643 return false; |
|
644 masm.j(Assembler::Overflow, ool->entry()); |
|
645 } else { |
|
646 if (!bailoutIf(Assembler::Overflow, ins->snapshot())) |
|
647 return false; |
|
648 } |
|
649 } |
|
650 return true; |
|
651 } |
|
652 |
|
653 bool |
|
654 CodeGeneratorX86Shared::visitSubI(LSubI *ins) |
|
655 { |
|
656 if (ins->rhs()->isConstant()) |
|
657 masm.subl(Imm32(ToInt32(ins->rhs())), ToOperand(ins->lhs())); |
|
658 else |
|
659 masm.subl(ToOperand(ins->rhs()), ToRegister(ins->lhs())); |
|
660 |
|
661 if (ins->snapshot()) { |
|
662 if (ins->recoversInput()) { |
|
663 OutOfLineUndoALUOperation *ool = new(alloc()) OutOfLineUndoALUOperation(ins); |
|
664 if (!addOutOfLineCode(ool)) |
|
665 return false; |
|
666 masm.j(Assembler::Overflow, ool->entry()); |
|
667 } else { |
|
668 if (!bailoutIf(Assembler::Overflow, ins->snapshot())) |
|
669 return false; |
|
670 } |
|
671 } |
|
672 return true; |
|
673 } |
|
674 |
|
675 bool |
|
676 CodeGeneratorX86Shared::visitOutOfLineUndoALUOperation(OutOfLineUndoALUOperation *ool) |
|
677 { |
|
678 LInstruction *ins = ool->ins(); |
|
679 Register reg = ToRegister(ins->getDef(0)); |
|
680 |
|
681 mozilla::DebugOnly<LAllocation *> lhs = ins->getOperand(0); |
|
682 LAllocation *rhs = ins->getOperand(1); |
|
683 |
|
684 JS_ASSERT(reg == ToRegister(lhs)); |
|
685 JS_ASSERT_IF(rhs->isGeneralReg(), reg != ToRegister(rhs)); |
|
686 |
|
687 // Undo the effect of the ALU operation, which was performed on the output |
|
688 // register and overflowed. Writing to the output register clobbered an |
|
689 // input reg, and the original value of the input needs to be recovered |
|
690 // to satisfy the constraint imposed by any RECOVERED_INPUT operands to |
|
691 // the bailout snapshot. |
|
692 |
|
693 if (rhs->isConstant()) { |
|
694 Imm32 constant(ToInt32(rhs)); |
|
695 if (ins->isAddI()) |
|
696 masm.subl(constant, reg); |
|
697 else |
|
698 masm.addl(constant, reg); |
|
699 } else { |
|
700 if (ins->isAddI()) |
|
701 masm.subl(ToOperand(rhs), reg); |
|
702 else |
|
703 masm.addl(ToOperand(rhs), reg); |
|
704 } |
|
705 |
|
706 return bailout(ool->ins()->snapshot()); |
|
707 } |
|
708 |
|
709 class MulNegativeZeroCheck : public OutOfLineCodeBase<CodeGeneratorX86Shared> |
|
710 { |
|
711 LMulI *ins_; |
|
712 |
|
713 public: |
|
714 MulNegativeZeroCheck(LMulI *ins) |
|
715 : ins_(ins) |
|
716 { } |
|
717 |
|
718 virtual bool accept(CodeGeneratorX86Shared *codegen) { |
|
719 return codegen->visitMulNegativeZeroCheck(this); |
|
720 } |
|
721 LMulI *ins() const { |
|
722 return ins_; |
|
723 } |
|
724 }; |
|
725 |
|
726 bool |
|
727 CodeGeneratorX86Shared::visitMulI(LMulI *ins) |
|
728 { |
|
729 const LAllocation *lhs = ins->lhs(); |
|
730 const LAllocation *rhs = ins->rhs(); |
|
731 MMul *mul = ins->mir(); |
|
732 JS_ASSERT_IF(mul->mode() == MMul::Integer, !mul->canBeNegativeZero() && !mul->canOverflow()); |
|
733 |
|
734 if (rhs->isConstant()) { |
|
735 // Bailout on -0.0 |
|
736 int32_t constant = ToInt32(rhs); |
|
737 if (mul->canBeNegativeZero() && constant <= 0) { |
|
738 Assembler::Condition bailoutCond = (constant == 0) ? Assembler::Signed : Assembler::Equal; |
|
739 masm.testl(ToRegister(lhs), ToRegister(lhs)); |
|
740 if (!bailoutIf(bailoutCond, ins->snapshot())) |
|
741 return false; |
|
742 } |
|
743 |
|
744 switch (constant) { |
|
745 case -1: |
|
746 masm.negl(ToOperand(lhs)); |
|
747 break; |
|
748 case 0: |
|
749 masm.xorl(ToOperand(lhs), ToRegister(lhs)); |
|
750 return true; // escape overflow check; |
|
751 case 1: |
|
752 // nop |
|
753 return true; // escape overflow check; |
|
754 case 2: |
|
755 masm.addl(ToOperand(lhs), ToRegister(lhs)); |
|
756 break; |
|
757 default: |
|
758 if (!mul->canOverflow() && constant > 0) { |
|
759 // Use shift if cannot overflow and constant is power of 2 |
|
760 int32_t shift = FloorLog2(constant); |
|
761 if ((1 << shift) == constant) { |
|
762 masm.shll(Imm32(shift), ToRegister(lhs)); |
|
763 return true; |
|
764 } |
|
765 } |
|
766 masm.imull(Imm32(ToInt32(rhs)), ToRegister(lhs)); |
|
767 } |
|
768 |
|
769 // Bailout on overflow |
|
770 if (mul->canOverflow() && !bailoutIf(Assembler::Overflow, ins->snapshot())) |
|
771 return false; |
|
772 } else { |
|
773 masm.imull(ToOperand(rhs), ToRegister(lhs)); |
|
774 |
|
775 // Bailout on overflow |
|
776 if (mul->canOverflow() && !bailoutIf(Assembler::Overflow, ins->snapshot())) |
|
777 return false; |
|
778 |
|
779 if (mul->canBeNegativeZero()) { |
|
780 // Jump to an OOL path if the result is 0. |
|
781 MulNegativeZeroCheck *ool = new(alloc()) MulNegativeZeroCheck(ins); |
|
782 if (!addOutOfLineCode(ool)) |
|
783 return false; |
|
784 |
|
785 masm.testl(ToRegister(lhs), ToRegister(lhs)); |
|
786 masm.j(Assembler::Zero, ool->entry()); |
|
787 masm.bind(ool->rejoin()); |
|
788 } |
|
789 } |
|
790 |
|
791 return true; |
|
792 } |
|
793 |
|
794 class ReturnZero : public OutOfLineCodeBase<CodeGeneratorX86Shared> |
|
795 { |
|
796 Register reg_; |
|
797 |
|
798 public: |
|
799 explicit ReturnZero(Register reg) |
|
800 : reg_(reg) |
|
801 { } |
|
802 |
|
803 virtual bool accept(CodeGeneratorX86Shared *codegen) { |
|
804 return codegen->visitReturnZero(this); |
|
805 } |
|
806 Register reg() const { |
|
807 return reg_; |
|
808 } |
|
809 }; |
|
810 |
|
811 bool |
|
812 CodeGeneratorX86Shared::visitReturnZero(ReturnZero *ool) |
|
813 { |
|
814 masm.mov(ImmWord(0), ool->reg()); |
|
815 masm.jmp(ool->rejoin()); |
|
816 return true; |
|
817 } |
|
818 |
|
819 bool |
|
820 CodeGeneratorX86Shared::visitUDivOrMod(LUDivOrMod *ins) |
|
821 { |
|
822 Register lhs = ToRegister(ins->lhs()); |
|
823 Register rhs = ToRegister(ins->rhs()); |
|
824 Register output = ToRegister(ins->output()); |
|
825 |
|
826 JS_ASSERT_IF(lhs != rhs, rhs != eax); |
|
827 JS_ASSERT(rhs != edx); |
|
828 JS_ASSERT_IF(output == eax, ToRegister(ins->remainder()) == edx); |
|
829 |
|
830 ReturnZero *ool = nullptr; |
|
831 |
|
832 // Put the lhs in eax. |
|
833 if (lhs != eax) |
|
834 masm.mov(lhs, eax); |
|
835 |
|
836 // Prevent divide by zero. |
|
837 if (ins->canBeDivideByZero()) { |
|
838 masm.testl(rhs, rhs); |
|
839 if (ins->mir()->isTruncated()) { |
|
840 if (!ool) |
|
841 ool = new(alloc()) ReturnZero(output); |
|
842 masm.j(Assembler::Zero, ool->entry()); |
|
843 } else { |
|
844 if (!bailoutIf(Assembler::Zero, ins->snapshot())) |
|
845 return false; |
|
846 } |
|
847 } |
|
848 |
|
849 // Zero extend the lhs into edx to make (edx:eax), since udiv is 64-bit. |
|
850 masm.mov(ImmWord(0), edx); |
|
851 masm.udiv(rhs); |
|
852 |
|
853 // Unsigned div or mod can return a value that's not a signed int32. |
|
854 // If our users aren't expecting that, bail. |
|
855 if (!ins->mir()->isTruncated()) { |
|
856 masm.testl(output, output); |
|
857 if (!bailoutIf(Assembler::Signed, ins->snapshot())) |
|
858 return false; |
|
859 } |
|
860 |
|
861 if (ool) { |
|
862 if (!addOutOfLineCode(ool)) |
|
863 return false; |
|
864 masm.bind(ool->rejoin()); |
|
865 } |
|
866 |
|
867 return true; |
|
868 } |
|
869 |
|
870 bool |
|
871 CodeGeneratorX86Shared::visitMulNegativeZeroCheck(MulNegativeZeroCheck *ool) |
|
872 { |
|
873 LMulI *ins = ool->ins(); |
|
874 Register result = ToRegister(ins->output()); |
|
875 Operand lhsCopy = ToOperand(ins->lhsCopy()); |
|
876 Operand rhs = ToOperand(ins->rhs()); |
|
877 JS_ASSERT_IF(lhsCopy.kind() == Operand::REG, lhsCopy.reg() != result.code()); |
|
878 |
|
879 // Result is -0 if lhs or rhs is negative. |
|
880 masm.movl(lhsCopy, result); |
|
881 masm.orl(rhs, result); |
|
882 if (!bailoutIf(Assembler::Signed, ins->snapshot())) |
|
883 return false; |
|
884 |
|
885 masm.mov(ImmWord(0), result); |
|
886 masm.jmp(ool->rejoin()); |
|
887 return true; |
|
888 } |
|
889 |
|
890 bool |
|
891 CodeGeneratorX86Shared::visitDivPowTwoI(LDivPowTwoI *ins) |
|
892 { |
|
893 Register lhs = ToRegister(ins->numerator()); |
|
894 mozilla::DebugOnly<Register> output = ToRegister(ins->output()); |
|
895 |
|
896 int32_t shift = ins->shift(); |
|
897 bool negativeDivisor = ins->negativeDivisor(); |
|
898 MDiv *mir = ins->mir(); |
|
899 |
|
900 // We use defineReuseInput so these should always be the same, which is |
|
901 // convenient since all of our instructions here are two-address. |
|
902 JS_ASSERT(lhs == output); |
|
903 |
|
904 if (!mir->isTruncated() && negativeDivisor) { |
|
905 // 0 divided by a negative number must return a double. |
|
906 masm.testl(lhs, lhs); |
|
907 if (!bailoutIf(Assembler::Zero, ins->snapshot())) |
|
908 return false; |
|
909 } |
|
910 |
|
911 if (shift != 0) { |
|
912 if (!mir->isTruncated()) { |
|
913 // If the remainder is != 0, bailout since this must be a double. |
|
914 masm.testl(lhs, Imm32(UINT32_MAX >> (32 - shift))); |
|
915 if (!bailoutIf(Assembler::NonZero, ins->snapshot())) |
|
916 return false; |
|
917 } |
|
918 |
|
919 // Adjust the value so that shifting produces a correctly rounded result |
|
920 // when the numerator is negative. See 10-1 "Signed Division by a Known |
|
921 // Power of 2" in Henry S. Warren, Jr.'s Hacker's Delight. |
|
922 if (mir->canBeNegativeDividend()) { |
|
923 Register lhsCopy = ToRegister(ins->numeratorCopy()); |
|
924 JS_ASSERT(lhsCopy != lhs); |
|
925 if (shift > 1) |
|
926 masm.sarl(Imm32(31), lhs); |
|
927 masm.shrl(Imm32(32 - shift), lhs); |
|
928 masm.addl(lhsCopy, lhs); |
|
929 } |
|
930 |
|
931 masm.sarl(Imm32(shift), lhs); |
|
932 if (negativeDivisor) |
|
933 masm.negl(lhs); |
|
934 } else if (shift == 0 && negativeDivisor) { |
|
935 // INT32_MIN / -1 overflows. |
|
936 masm.negl(lhs); |
|
937 if (!mir->isTruncated() && !bailoutIf(Assembler::Overflow, ins->snapshot())) |
|
938 return false; |
|
939 } |
|
940 |
|
941 return true; |
|
942 } |
|
943 |
|
944 bool |
|
945 CodeGeneratorX86Shared::visitDivOrModConstantI(LDivOrModConstantI *ins) { |
|
946 Register lhs = ToRegister(ins->numerator()); |
|
947 Register output = ToRegister(ins->output()); |
|
948 int32_t d = ins->denominator(); |
|
949 |
|
950 // This emits the division answer into edx or the modulus answer into eax. |
|
951 JS_ASSERT(output == eax || output == edx); |
|
952 JS_ASSERT(lhs != eax && lhs != edx); |
|
953 bool isDiv = (output == edx); |
|
954 |
|
955 // The absolute value of the denominator isn't a power of 2 (see LDivPowTwoI |
|
956 // and LModPowTwoI). |
|
957 JS_ASSERT((Abs(d) & (Abs(d) - 1)) != 0); |
|
958 |
|
959 // We will first divide by Abs(d), and negate the answer if d is negative. |
|
960 // If desired, this can be avoided by generalizing computeDivisionConstants. |
|
961 ReciprocalMulConstants rmc = computeDivisionConstants(Abs(d)); |
|
962 |
|
963 // As explained in the comments of computeDivisionConstants, we first compute |
|
964 // X >> (32 + shift), where X is either (rmc.multiplier * n) if the multiplier |
|
965 // is non-negative or (rmc.multiplier * n) + (2^32 * n) otherwise. This is the |
|
966 // desired division result if n is non-negative, and is one less than the result |
|
967 // otherwise. |
|
968 masm.movl(Imm32(rmc.multiplier), eax); |
|
969 masm.imull(lhs); |
|
970 if (rmc.multiplier < 0) |
|
971 masm.addl(lhs, edx); |
|
972 masm.sarl(Imm32(rmc.shiftAmount), edx); |
|
973 |
|
974 // We'll subtract -1 instead of adding 1, because (n < 0 ? -1 : 0) can be |
|
975 // computed with just a sign-extending shift of 31 bits. |
|
976 if (ins->canBeNegativeDividend()) { |
|
977 masm.movl(lhs, eax); |
|
978 masm.sarl(Imm32(31), eax); |
|
979 masm.subl(eax, edx); |
|
980 } |
|
981 |
|
982 // After this, edx contains the correct truncated division result. |
|
983 if (d < 0) |
|
984 masm.negl(edx); |
|
985 |
|
986 if (!isDiv) { |
|
987 masm.imull(Imm32(-d), edx, eax); |
|
988 masm.addl(lhs, eax); |
|
989 } |
|
990 |
|
991 if (!ins->mir()->isTruncated()) { |
|
992 if (isDiv) { |
|
993 // This is a division op. Multiply the obtained value by d to check if |
|
994 // the correct answer is an integer. This cannot overflow, since |d| > 1. |
|
995 masm.imull(Imm32(d), edx, eax); |
|
996 masm.cmpl(lhs, eax); |
|
997 if (!bailoutIf(Assembler::NotEqual, ins->snapshot())) |
|
998 return false; |
|
999 |
|
1000 // If lhs is zero and the divisor is negative, the answer should have |
|
1001 // been -0. |
|
1002 if (d < 0) { |
|
1003 masm.testl(lhs, lhs); |
|
1004 if (!bailoutIf(Assembler::Zero, ins->snapshot())) |
|
1005 return false; |
|
1006 } |
|
1007 } else if (ins->canBeNegativeDividend()) { |
|
1008 // This is a mod op. If the computed value is zero and lhs |
|
1009 // is negative, the answer should have been -0. |
|
1010 Label done; |
|
1011 |
|
1012 masm.cmpl(lhs, Imm32(0)); |
|
1013 masm.j(Assembler::GreaterThanOrEqual, &done); |
|
1014 |
|
1015 masm.testl(eax, eax); |
|
1016 if (!bailoutIf(Assembler::Zero, ins->snapshot())) |
|
1017 return false; |
|
1018 |
|
1019 masm.bind(&done); |
|
1020 } |
|
1021 } |
|
1022 |
|
1023 return true; |
|
1024 } |
|
1025 |
|
1026 bool |
|
1027 CodeGeneratorX86Shared::visitDivI(LDivI *ins) |
|
1028 { |
|
1029 Register remainder = ToRegister(ins->remainder()); |
|
1030 Register lhs = ToRegister(ins->lhs()); |
|
1031 Register rhs = ToRegister(ins->rhs()); |
|
1032 Register output = ToRegister(ins->output()); |
|
1033 |
|
1034 MDiv *mir = ins->mir(); |
|
1035 |
|
1036 JS_ASSERT_IF(lhs != rhs, rhs != eax); |
|
1037 JS_ASSERT(rhs != edx); |
|
1038 JS_ASSERT(remainder == edx); |
|
1039 JS_ASSERT(output == eax); |
|
1040 |
|
1041 Label done; |
|
1042 ReturnZero *ool = nullptr; |
|
1043 |
|
1044 // Put the lhs in eax, for either the negative overflow case or the regular |
|
1045 // divide case. |
|
1046 if (lhs != eax) |
|
1047 masm.mov(lhs, eax); |
|
1048 |
|
1049 // Handle divide by zero. |
|
1050 if (mir->canBeDivideByZero()) { |
|
1051 masm.testl(rhs, rhs); |
|
1052 if (mir->canTruncateInfinities()) { |
|
1053 // Truncated division by zero is zero (Infinity|0 == 0) |
|
1054 if (!ool) |
|
1055 ool = new(alloc()) ReturnZero(output); |
|
1056 masm.j(Assembler::Zero, ool->entry()); |
|
1057 } else { |
|
1058 JS_ASSERT(mir->fallible()); |
|
1059 if (!bailoutIf(Assembler::Zero, ins->snapshot())) |
|
1060 return false; |
|
1061 } |
|
1062 } |
|
1063 |
|
1064 // Handle an integer overflow exception from -2147483648 / -1. |
|
1065 if (mir->canBeNegativeOverflow()) { |
|
1066 Label notmin; |
|
1067 masm.cmpl(lhs, Imm32(INT32_MIN)); |
|
1068 masm.j(Assembler::NotEqual, ¬min); |
|
1069 masm.cmpl(rhs, Imm32(-1)); |
|
1070 if (mir->canTruncateOverflow()) { |
|
1071 // (-INT32_MIN)|0 == INT32_MIN and INT32_MIN is already in the |
|
1072 // output register (lhs == eax). |
|
1073 masm.j(Assembler::Equal, &done); |
|
1074 } else { |
|
1075 JS_ASSERT(mir->fallible()); |
|
1076 if (!bailoutIf(Assembler::Equal, ins->snapshot())) |
|
1077 return false; |
|
1078 } |
|
1079 masm.bind(¬min); |
|
1080 } |
|
1081 |
|
1082 // Handle negative 0. |
|
1083 if (!mir->canTruncateNegativeZero() && mir->canBeNegativeZero()) { |
|
1084 Label nonzero; |
|
1085 masm.testl(lhs, lhs); |
|
1086 masm.j(Assembler::NonZero, &nonzero); |
|
1087 masm.cmpl(rhs, Imm32(0)); |
|
1088 if (!bailoutIf(Assembler::LessThan, ins->snapshot())) |
|
1089 return false; |
|
1090 masm.bind(&nonzero); |
|
1091 } |
|
1092 |
|
1093 // Sign extend the lhs into edx to make (edx:eax), since idiv is 64-bit. |
|
1094 if (lhs != eax) |
|
1095 masm.mov(lhs, eax); |
|
1096 masm.cdq(); |
|
1097 masm.idiv(rhs); |
|
1098 |
|
1099 if (!mir->canTruncateRemainder()) { |
|
1100 // If the remainder is > 0, bailout since this must be a double. |
|
1101 masm.testl(remainder, remainder); |
|
1102 if (!bailoutIf(Assembler::NonZero, ins->snapshot())) |
|
1103 return false; |
|
1104 } |
|
1105 |
|
1106 masm.bind(&done); |
|
1107 |
|
1108 if (ool) { |
|
1109 if (!addOutOfLineCode(ool)) |
|
1110 return false; |
|
1111 masm.bind(ool->rejoin()); |
|
1112 } |
|
1113 |
|
1114 return true; |
|
1115 } |
|
1116 |
|
1117 bool |
|
1118 CodeGeneratorX86Shared::visitModPowTwoI(LModPowTwoI *ins) |
|
1119 { |
|
1120 Register lhs = ToRegister(ins->getOperand(0)); |
|
1121 int32_t shift = ins->shift(); |
|
1122 |
|
1123 Label negative; |
|
1124 |
|
1125 if (ins->mir()->canBeNegativeDividend()) { |
|
1126 // Switch based on sign of the lhs. |
|
1127 // Positive numbers are just a bitmask |
|
1128 masm.branchTest32(Assembler::Signed, lhs, lhs, &negative); |
|
1129 } |
|
1130 |
|
1131 masm.andl(Imm32((uint32_t(1) << shift) - 1), lhs); |
|
1132 |
|
1133 if (ins->mir()->canBeNegativeDividend()) { |
|
1134 Label done; |
|
1135 masm.jump(&done); |
|
1136 |
|
1137 // Negative numbers need a negate, bitmask, negate |
|
1138 masm.bind(&negative); |
|
1139 |
|
1140 // Unlike in the visitModI case, we are not computing the mod by means of a |
|
1141 // division. Therefore, the divisor = -1 case isn't problematic (the andl |
|
1142 // always returns 0, which is what we expect). |
|
1143 // |
|
1144 // The negl instruction overflows if lhs == INT32_MIN, but this is also not |
|
1145 // a problem: shift is at most 31, and so the andl also always returns 0. |
|
1146 masm.negl(lhs); |
|
1147 masm.andl(Imm32((uint32_t(1) << shift) - 1), lhs); |
|
1148 masm.negl(lhs); |
|
1149 |
|
1150 // Since a%b has the same sign as b, and a is negative in this branch, |
|
1151 // an answer of 0 means the correct result is actually -0. Bail out. |
|
1152 if (!ins->mir()->isTruncated() && !bailoutIf(Assembler::Zero, ins->snapshot())) |
|
1153 return false; |
|
1154 masm.bind(&done); |
|
1155 } |
|
1156 return true; |
|
1157 |
|
1158 } |
|
1159 |
|
1160 class ModOverflowCheck : public OutOfLineCodeBase<CodeGeneratorX86Shared> |
|
1161 { |
|
1162 Label done_; |
|
1163 LModI *ins_; |
|
1164 Register rhs_; |
|
1165 |
|
1166 public: |
|
1167 explicit ModOverflowCheck(LModI *ins, Register rhs) |
|
1168 : ins_(ins), rhs_(rhs) |
|
1169 { } |
|
1170 |
|
1171 virtual bool accept(CodeGeneratorX86Shared *codegen) { |
|
1172 return codegen->visitModOverflowCheck(this); |
|
1173 } |
|
1174 Label *done() { |
|
1175 return &done_; |
|
1176 } |
|
1177 LModI *ins() const { |
|
1178 return ins_; |
|
1179 } |
|
1180 Register rhs() const { |
|
1181 return rhs_; |
|
1182 } |
|
1183 }; |
|
1184 |
|
1185 bool |
|
1186 CodeGeneratorX86Shared::visitModOverflowCheck(ModOverflowCheck *ool) |
|
1187 { |
|
1188 masm.cmpl(ool->rhs(), Imm32(-1)); |
|
1189 if (ool->ins()->mir()->isTruncated()) { |
|
1190 masm.j(Assembler::NotEqual, ool->rejoin()); |
|
1191 masm.mov(ImmWord(0), edx); |
|
1192 masm.jmp(ool->done()); |
|
1193 } else { |
|
1194 if (!bailoutIf(Assembler::Equal, ool->ins()->snapshot())) |
|
1195 return false; |
|
1196 masm.jmp(ool->rejoin()); |
|
1197 } |
|
1198 return true; |
|
1199 } |
|
1200 |
|
1201 bool |
|
1202 CodeGeneratorX86Shared::visitModI(LModI *ins) |
|
1203 { |
|
1204 Register remainder = ToRegister(ins->remainder()); |
|
1205 Register lhs = ToRegister(ins->lhs()); |
|
1206 Register rhs = ToRegister(ins->rhs()); |
|
1207 |
|
1208 // Required to use idiv. |
|
1209 JS_ASSERT_IF(lhs != rhs, rhs != eax); |
|
1210 JS_ASSERT(rhs != edx); |
|
1211 JS_ASSERT(remainder == edx); |
|
1212 JS_ASSERT(ToRegister(ins->getTemp(0)) == eax); |
|
1213 |
|
1214 Label done; |
|
1215 ReturnZero *ool = nullptr; |
|
1216 ModOverflowCheck *overflow = nullptr; |
|
1217 |
|
1218 // Set up eax in preparation for doing a div. |
|
1219 if (lhs != eax) |
|
1220 masm.mov(lhs, eax); |
|
1221 |
|
1222 // Prevent divide by zero. |
|
1223 if (ins->mir()->canBeDivideByZero()) { |
|
1224 masm.testl(rhs, rhs); |
|
1225 if (ins->mir()->isTruncated()) { |
|
1226 if (!ool) |
|
1227 ool = new(alloc()) ReturnZero(edx); |
|
1228 masm.j(Assembler::Zero, ool->entry()); |
|
1229 } else { |
|
1230 if (!bailoutIf(Assembler::Zero, ins->snapshot())) |
|
1231 return false; |
|
1232 } |
|
1233 } |
|
1234 |
|
1235 Label negative; |
|
1236 |
|
1237 // Switch based on sign of the lhs. |
|
1238 if (ins->mir()->canBeNegativeDividend()) |
|
1239 masm.branchTest32(Assembler::Signed, lhs, lhs, &negative); |
|
1240 |
|
1241 // If lhs >= 0 then remainder = lhs % rhs. The remainder must be positive. |
|
1242 { |
|
1243 // Check if rhs is a power-of-two. |
|
1244 if (ins->mir()->canBePowerOfTwoDivisor()) { |
|
1245 JS_ASSERT(rhs != remainder); |
|
1246 |
|
1247 // Rhs y is a power-of-two if (y & (y-1)) == 0. Note that if |
|
1248 // y is any negative number other than INT32_MIN, both y and |
|
1249 // y-1 will have the sign bit set so these are never optimized |
|
1250 // as powers-of-two. If y is INT32_MIN, y-1 will be INT32_MAX |
|
1251 // and because lhs >= 0 at this point, lhs & INT32_MAX returns |
|
1252 // the correct value. |
|
1253 Label notPowerOfTwo; |
|
1254 masm.mov(rhs, remainder); |
|
1255 masm.subl(Imm32(1), remainder); |
|
1256 masm.branchTest32(Assembler::NonZero, remainder, rhs, ¬PowerOfTwo); |
|
1257 { |
|
1258 masm.andl(lhs, remainder); |
|
1259 masm.jmp(&done); |
|
1260 } |
|
1261 masm.bind(¬PowerOfTwo); |
|
1262 } |
|
1263 |
|
1264 // Since lhs >= 0, the sign-extension will be 0 |
|
1265 masm.mov(ImmWord(0), edx); |
|
1266 masm.idiv(rhs); |
|
1267 } |
|
1268 |
|
1269 // Otherwise, we have to beware of two special cases: |
|
1270 if (ins->mir()->canBeNegativeDividend()) { |
|
1271 masm.jump(&done); |
|
1272 |
|
1273 masm.bind(&negative); |
|
1274 |
|
1275 // Prevent an integer overflow exception from -2147483648 % -1 |
|
1276 Label notmin; |
|
1277 masm.cmpl(lhs, Imm32(INT32_MIN)); |
|
1278 overflow = new(alloc()) ModOverflowCheck(ins, rhs); |
|
1279 masm.j(Assembler::Equal, overflow->entry()); |
|
1280 masm.bind(overflow->rejoin()); |
|
1281 masm.cdq(); |
|
1282 masm.idiv(rhs); |
|
1283 |
|
1284 if (!ins->mir()->isTruncated()) { |
|
1285 // A remainder of 0 means that the rval must be -0, which is a double. |
|
1286 masm.testl(remainder, remainder); |
|
1287 if (!bailoutIf(Assembler::Zero, ins->snapshot())) |
|
1288 return false; |
|
1289 } |
|
1290 } |
|
1291 |
|
1292 masm.bind(&done); |
|
1293 |
|
1294 if (overflow) { |
|
1295 if (!addOutOfLineCode(overflow)) |
|
1296 return false; |
|
1297 masm.bind(overflow->done()); |
|
1298 } |
|
1299 |
|
1300 if (ool) { |
|
1301 if (!addOutOfLineCode(ool)) |
|
1302 return false; |
|
1303 masm.bind(ool->rejoin()); |
|
1304 } |
|
1305 |
|
1306 return true; |
|
1307 } |
|
1308 |
|
1309 bool |
|
1310 CodeGeneratorX86Shared::visitBitNotI(LBitNotI *ins) |
|
1311 { |
|
1312 const LAllocation *input = ins->getOperand(0); |
|
1313 JS_ASSERT(!input->isConstant()); |
|
1314 |
|
1315 masm.notl(ToOperand(input)); |
|
1316 return true; |
|
1317 } |
|
1318 |
|
1319 bool |
|
1320 CodeGeneratorX86Shared::visitBitOpI(LBitOpI *ins) |
|
1321 { |
|
1322 const LAllocation *lhs = ins->getOperand(0); |
|
1323 const LAllocation *rhs = ins->getOperand(1); |
|
1324 |
|
1325 switch (ins->bitop()) { |
|
1326 case JSOP_BITOR: |
|
1327 if (rhs->isConstant()) |
|
1328 masm.orl(Imm32(ToInt32(rhs)), ToOperand(lhs)); |
|
1329 else |
|
1330 masm.orl(ToOperand(rhs), ToRegister(lhs)); |
|
1331 break; |
|
1332 case JSOP_BITXOR: |
|
1333 if (rhs->isConstant()) |
|
1334 masm.xorl(Imm32(ToInt32(rhs)), ToOperand(lhs)); |
|
1335 else |
|
1336 masm.xorl(ToOperand(rhs), ToRegister(lhs)); |
|
1337 break; |
|
1338 case JSOP_BITAND: |
|
1339 if (rhs->isConstant()) |
|
1340 masm.andl(Imm32(ToInt32(rhs)), ToOperand(lhs)); |
|
1341 else |
|
1342 masm.andl(ToOperand(rhs), ToRegister(lhs)); |
|
1343 break; |
|
1344 default: |
|
1345 MOZ_ASSUME_UNREACHABLE("unexpected binary opcode"); |
|
1346 } |
|
1347 |
|
1348 return true; |
|
1349 } |
|
1350 |
|
1351 bool |
|
1352 CodeGeneratorX86Shared::visitShiftI(LShiftI *ins) |
|
1353 { |
|
1354 Register lhs = ToRegister(ins->lhs()); |
|
1355 const LAllocation *rhs = ins->rhs(); |
|
1356 |
|
1357 if (rhs->isConstant()) { |
|
1358 int32_t shift = ToInt32(rhs) & 0x1F; |
|
1359 switch (ins->bitop()) { |
|
1360 case JSOP_LSH: |
|
1361 if (shift) |
|
1362 masm.shll(Imm32(shift), lhs); |
|
1363 break; |
|
1364 case JSOP_RSH: |
|
1365 if (shift) |
|
1366 masm.sarl(Imm32(shift), lhs); |
|
1367 break; |
|
1368 case JSOP_URSH: |
|
1369 if (shift) { |
|
1370 masm.shrl(Imm32(shift), lhs); |
|
1371 } else if (ins->mir()->toUrsh()->fallible()) { |
|
1372 // x >>> 0 can overflow. |
|
1373 masm.testl(lhs, lhs); |
|
1374 if (!bailoutIf(Assembler::Signed, ins->snapshot())) |
|
1375 return false; |
|
1376 } |
|
1377 break; |
|
1378 default: |
|
1379 MOZ_ASSUME_UNREACHABLE("Unexpected shift op"); |
|
1380 } |
|
1381 } else { |
|
1382 JS_ASSERT(ToRegister(rhs) == ecx); |
|
1383 switch (ins->bitop()) { |
|
1384 case JSOP_LSH: |
|
1385 masm.shll_cl(lhs); |
|
1386 break; |
|
1387 case JSOP_RSH: |
|
1388 masm.sarl_cl(lhs); |
|
1389 break; |
|
1390 case JSOP_URSH: |
|
1391 masm.shrl_cl(lhs); |
|
1392 if (ins->mir()->toUrsh()->fallible()) { |
|
1393 // x >>> 0 can overflow. |
|
1394 masm.testl(lhs, lhs); |
|
1395 if (!bailoutIf(Assembler::Signed, ins->snapshot())) |
|
1396 return false; |
|
1397 } |
|
1398 break; |
|
1399 default: |
|
1400 MOZ_ASSUME_UNREACHABLE("Unexpected shift op"); |
|
1401 } |
|
1402 } |
|
1403 |
|
1404 return true; |
|
1405 } |
|
1406 |
|
1407 bool |
|
1408 CodeGeneratorX86Shared::visitUrshD(LUrshD *ins) |
|
1409 { |
|
1410 Register lhs = ToRegister(ins->lhs()); |
|
1411 JS_ASSERT(ToRegister(ins->temp()) == lhs); |
|
1412 |
|
1413 const LAllocation *rhs = ins->rhs(); |
|
1414 FloatRegister out = ToFloatRegister(ins->output()); |
|
1415 |
|
1416 if (rhs->isConstant()) { |
|
1417 int32_t shift = ToInt32(rhs) & 0x1F; |
|
1418 if (shift) |
|
1419 masm.shrl(Imm32(shift), lhs); |
|
1420 } else { |
|
1421 JS_ASSERT(ToRegister(rhs) == ecx); |
|
1422 masm.shrl_cl(lhs); |
|
1423 } |
|
1424 |
|
1425 masm.convertUInt32ToDouble(lhs, out); |
|
1426 return true; |
|
1427 } |
|
1428 |
|
1429 MoveOperand |
|
1430 CodeGeneratorX86Shared::toMoveOperand(const LAllocation *a) const |
|
1431 { |
|
1432 if (a->isGeneralReg()) |
|
1433 return MoveOperand(ToRegister(a)); |
|
1434 if (a->isFloatReg()) |
|
1435 return MoveOperand(ToFloatRegister(a)); |
|
1436 return MoveOperand(StackPointer, ToStackOffset(a)); |
|
1437 } |
|
1438 |
|
1439 class OutOfLineTableSwitch : public OutOfLineCodeBase<CodeGeneratorX86Shared> |
|
1440 { |
|
1441 MTableSwitch *mir_; |
|
1442 CodeLabel jumpLabel_; |
|
1443 |
|
1444 bool accept(CodeGeneratorX86Shared *codegen) { |
|
1445 return codegen->visitOutOfLineTableSwitch(this); |
|
1446 } |
|
1447 |
|
1448 public: |
|
1449 OutOfLineTableSwitch(MTableSwitch *mir) |
|
1450 : mir_(mir) |
|
1451 {} |
|
1452 |
|
1453 MTableSwitch *mir() const { |
|
1454 return mir_; |
|
1455 } |
|
1456 |
|
1457 CodeLabel *jumpLabel() { |
|
1458 return &jumpLabel_; |
|
1459 } |
|
1460 }; |
|
1461 |
|
1462 bool |
|
1463 CodeGeneratorX86Shared::visitOutOfLineTableSwitch(OutOfLineTableSwitch *ool) |
|
1464 { |
|
1465 MTableSwitch *mir = ool->mir(); |
|
1466 |
|
1467 masm.align(sizeof(void*)); |
|
1468 masm.bind(ool->jumpLabel()->src()); |
|
1469 if (!masm.addCodeLabel(*ool->jumpLabel())) |
|
1470 return false; |
|
1471 |
|
1472 for (size_t i = 0; i < mir->numCases(); i++) { |
|
1473 LBlock *caseblock = mir->getCase(i)->lir(); |
|
1474 Label *caseheader = caseblock->label(); |
|
1475 uint32_t caseoffset = caseheader->offset(); |
|
1476 |
|
1477 // The entries of the jump table need to be absolute addresses and thus |
|
1478 // must be patched after codegen is finished. |
|
1479 CodeLabel cl; |
|
1480 masm.writeCodePointer(cl.dest()); |
|
1481 cl.src()->bind(caseoffset); |
|
1482 if (!masm.addCodeLabel(cl)) |
|
1483 return false; |
|
1484 } |
|
1485 |
|
1486 return true; |
|
1487 } |
|
1488 |
|
1489 bool |
|
1490 CodeGeneratorX86Shared::emitTableSwitchDispatch(MTableSwitch *mir, const Register &index, |
|
1491 const Register &base) |
|
1492 { |
|
1493 Label *defaultcase = mir->getDefault()->lir()->label(); |
|
1494 |
|
1495 // Lower value with low value |
|
1496 if (mir->low() != 0) |
|
1497 masm.subl(Imm32(mir->low()), index); |
|
1498 |
|
1499 // Jump to default case if input is out of range |
|
1500 int32_t cases = mir->numCases(); |
|
1501 masm.cmpl(index, Imm32(cases)); |
|
1502 masm.j(AssemblerX86Shared::AboveOrEqual, defaultcase); |
|
1503 |
|
1504 // To fill in the CodeLabels for the case entries, we need to first |
|
1505 // generate the case entries (we don't yet know their offsets in the |
|
1506 // instruction stream). |
|
1507 OutOfLineTableSwitch *ool = new(alloc()) OutOfLineTableSwitch(mir); |
|
1508 if (!addOutOfLineCode(ool)) |
|
1509 return false; |
|
1510 |
|
1511 // Compute the position where a pointer to the right case stands. |
|
1512 masm.mov(ool->jumpLabel()->dest(), base); |
|
1513 Operand pointer = Operand(base, index, ScalePointer); |
|
1514 |
|
1515 // Jump to the right case |
|
1516 masm.jmp(pointer); |
|
1517 |
|
1518 return true; |
|
1519 } |
|
1520 |
|
1521 bool |
|
1522 CodeGeneratorX86Shared::visitMathD(LMathD *math) |
|
1523 { |
|
1524 FloatRegister lhs = ToFloatRegister(math->lhs()); |
|
1525 Operand rhs = ToOperand(math->rhs()); |
|
1526 |
|
1527 JS_ASSERT(ToFloatRegister(math->output()) == lhs); |
|
1528 |
|
1529 switch (math->jsop()) { |
|
1530 case JSOP_ADD: |
|
1531 masm.addsd(rhs, lhs); |
|
1532 break; |
|
1533 case JSOP_SUB: |
|
1534 masm.subsd(rhs, lhs); |
|
1535 break; |
|
1536 case JSOP_MUL: |
|
1537 masm.mulsd(rhs, lhs); |
|
1538 break; |
|
1539 case JSOP_DIV: |
|
1540 masm.divsd(rhs, lhs); |
|
1541 break; |
|
1542 default: |
|
1543 MOZ_ASSUME_UNREACHABLE("unexpected opcode"); |
|
1544 } |
|
1545 return true; |
|
1546 } |
|
1547 |
|
1548 bool |
|
1549 CodeGeneratorX86Shared::visitMathF(LMathF *math) |
|
1550 { |
|
1551 FloatRegister lhs = ToFloatRegister(math->lhs()); |
|
1552 Operand rhs = ToOperand(math->rhs()); |
|
1553 |
|
1554 JS_ASSERT(ToFloatRegister(math->output()) == lhs); |
|
1555 |
|
1556 switch (math->jsop()) { |
|
1557 case JSOP_ADD: |
|
1558 masm.addss(rhs, lhs); |
|
1559 break; |
|
1560 case JSOP_SUB: |
|
1561 masm.subss(rhs, lhs); |
|
1562 break; |
|
1563 case JSOP_MUL: |
|
1564 masm.mulss(rhs, lhs); |
|
1565 break; |
|
1566 case JSOP_DIV: |
|
1567 masm.divss(rhs, lhs); |
|
1568 break; |
|
1569 default: |
|
1570 MOZ_ASSUME_UNREACHABLE("unexpected opcode"); |
|
1571 return false; |
|
1572 } |
|
1573 return true; |
|
1574 } |
|
1575 |
|
1576 bool |
|
1577 CodeGeneratorX86Shared::visitFloor(LFloor *lir) |
|
1578 { |
|
1579 FloatRegister input = ToFloatRegister(lir->input()); |
|
1580 FloatRegister scratch = ScratchFloatReg; |
|
1581 Register output = ToRegister(lir->output()); |
|
1582 |
|
1583 Label bailout; |
|
1584 |
|
1585 if (AssemblerX86Shared::HasSSE41()) { |
|
1586 // Bail on negative-zero. |
|
1587 masm.branchNegativeZero(input, output, &bailout); |
|
1588 if (!bailoutFrom(&bailout, lir->snapshot())) |
|
1589 return false; |
|
1590 |
|
1591 // Round toward -Infinity. |
|
1592 masm.roundsd(input, scratch, JSC::X86Assembler::RoundDown); |
|
1593 |
|
1594 masm.cvttsd2si(scratch, output); |
|
1595 masm.cmp32(output, Imm32(INT_MIN)); |
|
1596 if (!bailoutIf(Assembler::Equal, lir->snapshot())) |
|
1597 return false; |
|
1598 } else { |
|
1599 Label negative, end; |
|
1600 |
|
1601 // Branch to a slow path for negative inputs. Doesn't catch NaN or -0. |
|
1602 masm.xorpd(scratch, scratch); |
|
1603 masm.branchDouble(Assembler::DoubleLessThan, input, scratch, &negative); |
|
1604 |
|
1605 // Bail on negative-zero. |
|
1606 masm.branchNegativeZero(input, output, &bailout); |
|
1607 if (!bailoutFrom(&bailout, lir->snapshot())) |
|
1608 return false; |
|
1609 |
|
1610 // Input is non-negative, so truncation correctly rounds. |
|
1611 masm.cvttsd2si(input, output); |
|
1612 masm.cmp32(output, Imm32(INT_MIN)); |
|
1613 if (!bailoutIf(Assembler::Equal, lir->snapshot())) |
|
1614 return false; |
|
1615 |
|
1616 masm.jump(&end); |
|
1617 |
|
1618 // Input is negative, but isn't -0. |
|
1619 // Negative values go on a comparatively expensive path, since no |
|
1620 // native rounding mode matches JS semantics. Still better than callVM. |
|
1621 masm.bind(&negative); |
|
1622 { |
|
1623 // Truncate and round toward zero. |
|
1624 // This is off-by-one for everything but integer-valued inputs. |
|
1625 masm.cvttsd2si(input, output); |
|
1626 masm.cmp32(output, Imm32(INT_MIN)); |
|
1627 if (!bailoutIf(Assembler::Equal, lir->snapshot())) |
|
1628 return false; |
|
1629 |
|
1630 // Test whether the input double was integer-valued. |
|
1631 masm.convertInt32ToDouble(output, scratch); |
|
1632 masm.branchDouble(Assembler::DoubleEqualOrUnordered, input, scratch, &end); |
|
1633 |
|
1634 // Input is not integer-valued, so we rounded off-by-one in the |
|
1635 // wrong direction. Correct by subtraction. |
|
1636 masm.subl(Imm32(1), output); |
|
1637 // Cannot overflow: output was already checked against INT_MIN. |
|
1638 } |
|
1639 |
|
1640 masm.bind(&end); |
|
1641 } |
|
1642 return true; |
|
1643 } |
|
1644 |
|
1645 bool |
|
1646 CodeGeneratorX86Shared::visitFloorF(LFloorF *lir) |
|
1647 { |
|
1648 FloatRegister input = ToFloatRegister(lir->input()); |
|
1649 FloatRegister scratch = ScratchFloatReg; |
|
1650 Register output = ToRegister(lir->output()); |
|
1651 |
|
1652 Label bailout; |
|
1653 |
|
1654 if (AssemblerX86Shared::HasSSE41()) { |
|
1655 // Bail on negative-zero. |
|
1656 masm.branchNegativeZeroFloat32(input, output, &bailout); |
|
1657 if (!bailoutFrom(&bailout, lir->snapshot())) |
|
1658 return false; |
|
1659 |
|
1660 // Round toward -Infinity. |
|
1661 masm.roundss(input, scratch, JSC::X86Assembler::RoundDown); |
|
1662 |
|
1663 masm.cvttss2si(scratch, output); |
|
1664 masm.cmp32(output, Imm32(INT_MIN)); |
|
1665 if (!bailoutIf(Assembler::Equal, lir->snapshot())) |
|
1666 return false; |
|
1667 } else { |
|
1668 Label negative, end; |
|
1669 |
|
1670 // Branch to a slow path for negative inputs. Doesn't catch NaN or -0. |
|
1671 masm.xorps(scratch, scratch); |
|
1672 masm.branchFloat(Assembler::DoubleLessThan, input, scratch, &negative); |
|
1673 |
|
1674 // Bail on negative-zero. |
|
1675 masm.branchNegativeZeroFloat32(input, output, &bailout); |
|
1676 if (!bailoutFrom(&bailout, lir->snapshot())) |
|
1677 return false; |
|
1678 |
|
1679 // Input is non-negative, so truncation correctly rounds. |
|
1680 masm.cvttss2si(input, output); |
|
1681 masm.cmp32(output, Imm32(INT_MIN)); |
|
1682 if (!bailoutIf(Assembler::Equal, lir->snapshot())) |
|
1683 return false; |
|
1684 |
|
1685 masm.jump(&end); |
|
1686 |
|
1687 // Input is negative, but isn't -0. |
|
1688 // Negative values go on a comparatively expensive path, since no |
|
1689 // native rounding mode matches JS semantics. Still better than callVM. |
|
1690 masm.bind(&negative); |
|
1691 { |
|
1692 // Truncate and round toward zero. |
|
1693 // This is off-by-one for everything but integer-valued inputs. |
|
1694 masm.cvttss2si(input, output); |
|
1695 masm.cmp32(output, Imm32(INT_MIN)); |
|
1696 if (!bailoutIf(Assembler::Equal, lir->snapshot())) |
|
1697 return false; |
|
1698 |
|
1699 // Test whether the input double was integer-valued. |
|
1700 masm.convertInt32ToFloat32(output, scratch); |
|
1701 masm.branchFloat(Assembler::DoubleEqualOrUnordered, input, scratch, &end); |
|
1702 |
|
1703 // Input is not integer-valued, so we rounded off-by-one in the |
|
1704 // wrong direction. Correct by subtraction. |
|
1705 masm.subl(Imm32(1), output); |
|
1706 // Cannot overflow: output was already checked against INT_MIN. |
|
1707 } |
|
1708 |
|
1709 masm.bind(&end); |
|
1710 } |
|
1711 return true; |
|
1712 } |
|
1713 |
|
1714 bool |
|
1715 CodeGeneratorX86Shared::visitRound(LRound *lir) |
|
1716 { |
|
1717 FloatRegister input = ToFloatRegister(lir->input()); |
|
1718 FloatRegister temp = ToFloatRegister(lir->temp()); |
|
1719 FloatRegister scratch = ScratchFloatReg; |
|
1720 Register output = ToRegister(lir->output()); |
|
1721 |
|
1722 Label negative, end, bailout; |
|
1723 |
|
1724 // Load 0.5 in the temp register. |
|
1725 masm.loadConstantDouble(0.5, temp); |
|
1726 |
|
1727 // Branch to a slow path for negative inputs. Doesn't catch NaN or -0. |
|
1728 masm.xorpd(scratch, scratch); |
|
1729 masm.branchDouble(Assembler::DoubleLessThan, input, scratch, &negative); |
|
1730 |
|
1731 // Bail on negative-zero. |
|
1732 masm.branchNegativeZero(input, output, &bailout); |
|
1733 if (!bailoutFrom(&bailout, lir->snapshot())) |
|
1734 return false; |
|
1735 |
|
1736 // Input is non-negative. Add 0.5 and truncate, rounding down. Note that we |
|
1737 // have to add the input to the temp register (which contains 0.5) because |
|
1738 // we're not allowed to modify the input register. |
|
1739 masm.addsd(input, temp); |
|
1740 |
|
1741 masm.cvttsd2si(temp, output); |
|
1742 masm.cmp32(output, Imm32(INT_MIN)); |
|
1743 if (!bailoutIf(Assembler::Equal, lir->snapshot())) |
|
1744 return false; |
|
1745 |
|
1746 masm.jump(&end); |
|
1747 |
|
1748 |
|
1749 // Input is negative, but isn't -0. |
|
1750 masm.bind(&negative); |
|
1751 |
|
1752 if (AssemblerX86Shared::HasSSE41()) { |
|
1753 // Add 0.5 and round toward -Infinity. The result is stored in the temp |
|
1754 // register (currently contains 0.5). |
|
1755 masm.addsd(input, temp); |
|
1756 masm.roundsd(temp, scratch, JSC::X86Assembler::RoundDown); |
|
1757 |
|
1758 // Truncate. |
|
1759 masm.cvttsd2si(scratch, output); |
|
1760 masm.cmp32(output, Imm32(INT_MIN)); |
|
1761 if (!bailoutIf(Assembler::Equal, lir->snapshot())) |
|
1762 return false; |
|
1763 |
|
1764 // If the result is positive zero, then the actual result is -0. Bail. |
|
1765 // Otherwise, the truncation will have produced the correct negative integer. |
|
1766 masm.testl(output, output); |
|
1767 if (!bailoutIf(Assembler::Zero, lir->snapshot())) |
|
1768 return false; |
|
1769 |
|
1770 } else { |
|
1771 masm.addsd(input, temp); |
|
1772 |
|
1773 // Round toward -Infinity without the benefit of ROUNDSD. |
|
1774 { |
|
1775 // If input + 0.5 >= 0, input is a negative number >= -0.5 and the result is -0. |
|
1776 masm.compareDouble(Assembler::DoubleGreaterThanOrEqual, temp, scratch); |
|
1777 if (!bailoutIf(Assembler::DoubleGreaterThanOrEqual, lir->snapshot())) |
|
1778 return false; |
|
1779 |
|
1780 // Truncate and round toward zero. |
|
1781 // This is off-by-one for everything but integer-valued inputs. |
|
1782 masm.cvttsd2si(temp, output); |
|
1783 masm.cmp32(output, Imm32(INT_MIN)); |
|
1784 if (!bailoutIf(Assembler::Equal, lir->snapshot())) |
|
1785 return false; |
|
1786 |
|
1787 // Test whether the truncated double was integer-valued. |
|
1788 masm.convertInt32ToDouble(output, scratch); |
|
1789 masm.branchDouble(Assembler::DoubleEqualOrUnordered, temp, scratch, &end); |
|
1790 |
|
1791 // Input is not integer-valued, so we rounded off-by-one in the |
|
1792 // wrong direction. Correct by subtraction. |
|
1793 masm.subl(Imm32(1), output); |
|
1794 // Cannot overflow: output was already checked against INT_MIN. |
|
1795 } |
|
1796 } |
|
1797 |
|
1798 masm.bind(&end); |
|
1799 return true; |
|
1800 } |
|
1801 |
|
1802 bool |
|
1803 CodeGeneratorX86Shared::visitRoundF(LRoundF *lir) |
|
1804 { |
|
1805 FloatRegister input = ToFloatRegister(lir->input()); |
|
1806 FloatRegister temp = ToFloatRegister(lir->temp()); |
|
1807 FloatRegister scratch = ScratchFloatReg; |
|
1808 Register output = ToRegister(lir->output()); |
|
1809 |
|
1810 Label negative, end, bailout; |
|
1811 |
|
1812 // Load 0.5 in the temp register. |
|
1813 masm.loadConstantFloat32(0.5f, temp); |
|
1814 |
|
1815 // Branch to a slow path for negative inputs. Doesn't catch NaN or -0. |
|
1816 masm.xorps(scratch, scratch); |
|
1817 masm.branchFloat(Assembler::DoubleLessThan, input, scratch, &negative); |
|
1818 |
|
1819 // Bail on negative-zero. |
|
1820 masm.branchNegativeZeroFloat32(input, output, &bailout); |
|
1821 if (!bailoutFrom(&bailout, lir->snapshot())) |
|
1822 return false; |
|
1823 |
|
1824 // Input is non-negative. Add 0.5 and truncate, rounding down. Note that we |
|
1825 // have to add the input to the temp register (which contains 0.5) because |
|
1826 // we're not allowed to modify the input register. |
|
1827 masm.addss(input, temp); |
|
1828 |
|
1829 masm.cvttss2si(temp, output); |
|
1830 masm.cmp32(output, Imm32(INT_MIN)); |
|
1831 if (!bailoutIf(Assembler::Equal, lir->snapshot())) |
|
1832 return false; |
|
1833 |
|
1834 masm.jump(&end); |
|
1835 |
|
1836 |
|
1837 // Input is negative, but isn't -0. |
|
1838 masm.bind(&negative); |
|
1839 |
|
1840 if (AssemblerX86Shared::HasSSE41()) { |
|
1841 // Add 0.5 and round toward -Infinity. The result is stored in the temp |
|
1842 // register (currently contains 0.5). |
|
1843 masm.addss(input, temp); |
|
1844 masm.roundss(temp, scratch, JSC::X86Assembler::RoundDown); |
|
1845 |
|
1846 // Truncate. |
|
1847 masm.cvttss2si(scratch, output); |
|
1848 masm.cmp32(output, Imm32(INT_MIN)); |
|
1849 if (!bailoutIf(Assembler::Equal, lir->snapshot())) |
|
1850 return false; |
|
1851 |
|
1852 // If the result is positive zero, then the actual result is -0. Bail. |
|
1853 // Otherwise, the truncation will have produced the correct negative integer. |
|
1854 masm.testl(output, output); |
|
1855 if (!bailoutIf(Assembler::Zero, lir->snapshot())) |
|
1856 return false; |
|
1857 |
|
1858 } else { |
|
1859 masm.addss(input, temp); |
|
1860 // Round toward -Infinity without the benefit of ROUNDSS. |
|
1861 { |
|
1862 // If input + 0.5 >= 0, input is a negative number >= -0.5 and the result is -0. |
|
1863 masm.compareFloat(Assembler::DoubleGreaterThanOrEqual, temp, scratch); |
|
1864 if (!bailoutIf(Assembler::DoubleGreaterThanOrEqual, lir->snapshot())) |
|
1865 return false; |
|
1866 |
|
1867 // Truncate and round toward zero. |
|
1868 // This is off-by-one for everything but integer-valued inputs. |
|
1869 masm.cvttss2si(temp, output); |
|
1870 masm.cmp32(output, Imm32(INT_MIN)); |
|
1871 if (!bailoutIf(Assembler::Equal, lir->snapshot())) |
|
1872 return false; |
|
1873 |
|
1874 // Test whether the truncated double was integer-valued. |
|
1875 masm.convertInt32ToFloat32(output, scratch); |
|
1876 masm.branchFloat(Assembler::DoubleEqualOrUnordered, temp, scratch, &end); |
|
1877 |
|
1878 // Input is not integer-valued, so we rounded off-by-one in the |
|
1879 // wrong direction. Correct by subtraction. |
|
1880 masm.subl(Imm32(1), output); |
|
1881 // Cannot overflow: output was already checked against INT_MIN. |
|
1882 } |
|
1883 } |
|
1884 |
|
1885 masm.bind(&end); |
|
1886 return true; |
|
1887 } |
|
1888 |
|
1889 bool |
|
1890 CodeGeneratorX86Shared::visitGuardShape(LGuardShape *guard) |
|
1891 { |
|
1892 Register obj = ToRegister(guard->input()); |
|
1893 masm.cmpPtr(Operand(obj, JSObject::offsetOfShape()), ImmGCPtr(guard->mir()->shape())); |
|
1894 |
|
1895 return bailoutIf(Assembler::NotEqual, guard->snapshot()); |
|
1896 } |
|
1897 |
|
1898 bool |
|
1899 CodeGeneratorX86Shared::visitGuardObjectType(LGuardObjectType *guard) |
|
1900 { |
|
1901 Register obj = ToRegister(guard->input()); |
|
1902 masm.cmpPtr(Operand(obj, JSObject::offsetOfType()), ImmGCPtr(guard->mir()->typeObject())); |
|
1903 |
|
1904 Assembler::Condition cond = |
|
1905 guard->mir()->bailOnEquality() ? Assembler::Equal : Assembler::NotEqual; |
|
1906 return bailoutIf(cond, guard->snapshot()); |
|
1907 } |
|
1908 |
|
1909 bool |
|
1910 CodeGeneratorX86Shared::visitGuardClass(LGuardClass *guard) |
|
1911 { |
|
1912 Register obj = ToRegister(guard->input()); |
|
1913 Register tmp = ToRegister(guard->tempInt()); |
|
1914 |
|
1915 masm.loadPtr(Address(obj, JSObject::offsetOfType()), tmp); |
|
1916 masm.cmpPtr(Operand(tmp, types::TypeObject::offsetOfClasp()), ImmPtr(guard->mir()->getClass())); |
|
1917 if (!bailoutIf(Assembler::NotEqual, guard->snapshot())) |
|
1918 return false; |
|
1919 return true; |
|
1920 } |
|
1921 |
|
1922 bool |
|
1923 CodeGeneratorX86Shared::visitEffectiveAddress(LEffectiveAddress *ins) |
|
1924 { |
|
1925 const MEffectiveAddress *mir = ins->mir(); |
|
1926 Register base = ToRegister(ins->base()); |
|
1927 Register index = ToRegister(ins->index()); |
|
1928 Register output = ToRegister(ins->output()); |
|
1929 masm.leal(Operand(base, index, mir->scale(), mir->displacement()), output); |
|
1930 return true; |
|
1931 } |
|
1932 |
|
1933 Operand |
|
1934 CodeGeneratorX86Shared::createArrayElementOperand(Register elements, const LAllocation *index) |
|
1935 { |
|
1936 if (index->isConstant()) |
|
1937 return Operand(elements, ToInt32(index) * sizeof(js::Value)); |
|
1938 |
|
1939 return Operand(elements, ToRegister(index), TimesEight); |
|
1940 } |
|
1941 bool |
|
1942 CodeGeneratorX86Shared::generateInvalidateEpilogue() |
|
1943 { |
|
1944 // Ensure that there is enough space in the buffer for the OsiPoint |
|
1945 // patching to occur. Otherwise, we could overwrite the invalidation |
|
1946 // epilogue. |
|
1947 for (size_t i = 0; i < sizeof(void *); i+= Assembler::nopSize()) |
|
1948 masm.nop(); |
|
1949 |
|
1950 masm.bind(&invalidate_); |
|
1951 |
|
1952 // Push the Ion script onto the stack (when we determine what that pointer is). |
|
1953 invalidateEpilogueData_ = masm.pushWithPatch(ImmWord(uintptr_t(-1))); |
|
1954 JitCode *thunk = gen->jitRuntime()->getInvalidationThunk(); |
|
1955 |
|
1956 masm.call(thunk); |
|
1957 |
|
1958 // We should never reach this point in JIT code -- the invalidation thunk should |
|
1959 // pop the invalidated JS frame and return directly to its caller. |
|
1960 masm.assumeUnreachable("Should have returned directly to its caller instead of here."); |
|
1961 return true; |
|
1962 } |
|
1963 |
|
1964 bool |
|
1965 CodeGeneratorX86Shared::visitNegI(LNegI *ins) |
|
1966 { |
|
1967 Register input = ToRegister(ins->input()); |
|
1968 JS_ASSERT(input == ToRegister(ins->output())); |
|
1969 |
|
1970 masm.neg32(input); |
|
1971 return true; |
|
1972 } |
|
1973 |
|
1974 bool |
|
1975 CodeGeneratorX86Shared::visitNegD(LNegD *ins) |
|
1976 { |
|
1977 FloatRegister input = ToFloatRegister(ins->input()); |
|
1978 JS_ASSERT(input == ToFloatRegister(ins->output())); |
|
1979 |
|
1980 masm.negateDouble(input); |
|
1981 return true; |
|
1982 } |
|
1983 |
|
1984 bool |
|
1985 CodeGeneratorX86Shared::visitNegF(LNegF *ins) |
|
1986 { |
|
1987 FloatRegister input = ToFloatRegister(ins->input()); |
|
1988 JS_ASSERT(input == ToFloatRegister(ins->output())); |
|
1989 |
|
1990 masm.negateFloat(input); |
|
1991 return true; |
|
1992 } |
|
1993 |
|
1994 bool |
|
1995 CodeGeneratorX86Shared::visitForkJoinGetSlice(LForkJoinGetSlice *ins) |
|
1996 { |
|
1997 MOZ_ASSERT(gen->info().executionMode() == ParallelExecution); |
|
1998 MOZ_ASSERT(ToRegister(ins->forkJoinContext()) == ForkJoinGetSliceReg_cx); |
|
1999 MOZ_ASSERT(ToRegister(ins->temp1()) == eax); |
|
2000 MOZ_ASSERT(ToRegister(ins->temp2()) == edx); |
|
2001 MOZ_ASSERT(ToRegister(ins->temp3()) == ForkJoinGetSliceReg_temp0); |
|
2002 MOZ_ASSERT(ToRegister(ins->temp4()) == ForkJoinGetSliceReg_temp1); |
|
2003 MOZ_ASSERT(ToRegister(ins->output()) == ForkJoinGetSliceReg_output); |
|
2004 |
|
2005 masm.call(gen->jitRuntime()->forkJoinGetSliceStub()); |
|
2006 return true; |
|
2007 } |
|
2008 |
|
2009 JitCode * |
|
2010 JitRuntime::generateForkJoinGetSliceStub(JSContext *cx) |
|
2011 { |
|
2012 #ifdef JS_THREADSAFE |
|
2013 MacroAssembler masm(cx); |
|
2014 |
|
2015 // We need two fixed temps. We need to fix eax for cmpxchg, and edx for |
|
2016 // div. |
|
2017 Register cxReg = ForkJoinGetSliceReg_cx, worker = cxReg; |
|
2018 Register pool = ForkJoinGetSliceReg_temp0; |
|
2019 Register bounds = ForkJoinGetSliceReg_temp1; |
|
2020 Register output = ForkJoinGetSliceReg_output; |
|
2021 |
|
2022 MOZ_ASSERT(worker != eax && worker != edx); |
|
2023 MOZ_ASSERT(pool != eax && pool != edx); |
|
2024 MOZ_ASSERT(bounds != eax && bounds != edx); |
|
2025 MOZ_ASSERT(output != eax && output != edx); |
|
2026 |
|
2027 Label stealWork, noMoreWork, gotSlice; |
|
2028 Operand workerSliceBounds(Address(worker, ThreadPoolWorker::offsetOfSliceBounds())); |
|
2029 |
|
2030 // Clobber cx to load the worker. |
|
2031 masm.push(cxReg); |
|
2032 masm.loadPtr(Address(cxReg, ForkJoinContext::offsetOfWorker()), worker); |
|
2033 |
|
2034 // Load the thread pool, which is used in all cases below. |
|
2035 masm.loadThreadPool(pool); |
|
2036 |
|
2037 { |
|
2038 // Try to get a slice from the current thread. |
|
2039 Label getOwnSliceLoopHead; |
|
2040 masm.bind(&getOwnSliceLoopHead); |
|
2041 |
|
2042 // Load the slice bounds for the current thread. |
|
2043 masm.loadSliceBounds(worker, bounds); |
|
2044 |
|
2045 // The slice bounds is a uint32 composed from two uint16s: |
|
2046 // [ from , to ] |
|
2047 // ^~~~ ^~ |
|
2048 // upper 16 bits | lower 16 bits |
|
2049 masm.move32(bounds, output); |
|
2050 masm.shrl(Imm32(16), output); |
|
2051 |
|
2052 // If we don't have any slices left ourselves, move on to stealing. |
|
2053 masm.branch16(Assembler::Equal, output, bounds, &stealWork); |
|
2054 |
|
2055 // If we still have work, try to CAS [ from+1, to ]. |
|
2056 masm.move32(bounds, edx); |
|
2057 masm.add32(Imm32(0x10000), edx); |
|
2058 masm.move32(bounds, eax); |
|
2059 masm.atomic_cmpxchg32(edx, workerSliceBounds, eax); |
|
2060 masm.j(Assembler::NonZero, &getOwnSliceLoopHead); |
|
2061 |
|
2062 // If the CAS succeeded, return |from| in output. |
|
2063 masm.jump(&gotSlice); |
|
2064 } |
|
2065 |
|
2066 // Try to steal work. |
|
2067 masm.bind(&stealWork); |
|
2068 |
|
2069 // It's not technically correct to test whether work-stealing is turned on |
|
2070 // only during stub-generation time, but it's a DEBUG only thing. |
|
2071 if (cx->runtime()->threadPool.workStealing()) { |
|
2072 Label stealWorkLoopHead; |
|
2073 masm.bind(&stealWorkLoopHead); |
|
2074 |
|
2075 // Check if we have work. |
|
2076 masm.branch32(Assembler::Equal, |
|
2077 Address(pool, ThreadPool::offsetOfPendingSlices()), |
|
2078 Imm32(0), &noMoreWork); |
|
2079 |
|
2080 // Get an id at random. The following is an inline of |
|
2081 // the 32-bit xorshift in ThreadPoolWorker::randomWorker(). |
|
2082 { |
|
2083 // Reload the current worker. |
|
2084 masm.loadPtr(Address(StackPointer, 0), cxReg); |
|
2085 masm.loadPtr(Address(cxReg, ForkJoinContext::offsetOfWorker()), worker); |
|
2086 |
|
2087 // Perform the xorshift to get a random number in eax, using edx |
|
2088 // as a temp. |
|
2089 Address rngState(worker, ThreadPoolWorker::offsetOfSchedulerRNGState()); |
|
2090 masm.load32(rngState, eax); |
|
2091 masm.move32(eax, edx); |
|
2092 masm.shll(Imm32(ThreadPoolWorker::XORSHIFT_A), eax); |
|
2093 masm.xor32(edx, eax); |
|
2094 masm.move32(eax, edx); |
|
2095 masm.shrl(Imm32(ThreadPoolWorker::XORSHIFT_B), eax); |
|
2096 masm.xor32(edx, eax); |
|
2097 masm.move32(eax, edx); |
|
2098 masm.shll(Imm32(ThreadPoolWorker::XORSHIFT_C), eax); |
|
2099 masm.xor32(edx, eax); |
|
2100 masm.store32(eax, rngState); |
|
2101 |
|
2102 // Compute the random worker id by computing % numWorkers. Reuse |
|
2103 // output as a temp. |
|
2104 masm.move32(Imm32(0), edx); |
|
2105 masm.move32(Imm32(cx->runtime()->threadPool.numWorkers()), output); |
|
2106 masm.udiv(output); |
|
2107 } |
|
2108 |
|
2109 // Load the worker from the workers array. |
|
2110 masm.loadPtr(Address(pool, ThreadPool::offsetOfWorkers()), worker); |
|
2111 masm.loadPtr(BaseIndex(worker, edx, ScalePointer), worker); |
|
2112 |
|
2113 // Try to get a slice from the designated victim worker. |
|
2114 Label stealSliceFromWorkerLoopHead; |
|
2115 masm.bind(&stealSliceFromWorkerLoopHead); |
|
2116 |
|
2117 // Load the slice bounds and decompose for the victim worker. |
|
2118 masm.loadSliceBounds(worker, bounds); |
|
2119 masm.move32(bounds, eax); |
|
2120 masm.shrl(Imm32(16), eax); |
|
2121 |
|
2122 // If the victim worker has no more slices left, find another worker. |
|
2123 masm.branch16(Assembler::Equal, eax, bounds, &stealWorkLoopHead); |
|
2124 |
|
2125 // If the victim worker still has work, try to CAS [ from, to-1 ]. |
|
2126 masm.move32(bounds, output); |
|
2127 masm.sub32(Imm32(1), output); |
|
2128 masm.move32(bounds, eax); |
|
2129 masm.atomic_cmpxchg32(output, workerSliceBounds, eax); |
|
2130 masm.j(Assembler::NonZero, &stealSliceFromWorkerLoopHead); |
|
2131 |
|
2132 // If the CAS succeeded, return |to-1| in output. |
|
2133 #ifdef DEBUG |
|
2134 masm.atomic_inc32(Operand(Address(pool, ThreadPool::offsetOfStolenSlices()))); |
|
2135 #endif |
|
2136 // Copies lower 16 bits only. |
|
2137 masm.movzwl(output, output); |
|
2138 } |
|
2139 |
|
2140 // If we successfully got a slice, decrement pool->pendingSlices_ and |
|
2141 // return the slice. |
|
2142 masm.bind(&gotSlice); |
|
2143 masm.atomic_dec32(Operand(Address(pool, ThreadPool::offsetOfPendingSlices()))); |
|
2144 masm.pop(cxReg); |
|
2145 masm.ret(); |
|
2146 |
|
2147 // There's no more slices to give out, return a sentinel value. |
|
2148 masm.bind(&noMoreWork); |
|
2149 masm.move32(Imm32(ThreadPool::MAX_SLICE_ID), output); |
|
2150 masm.pop(cxReg); |
|
2151 masm.ret(); |
|
2152 |
|
2153 Linker linker(masm); |
|
2154 JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE); |
|
2155 |
|
2156 #ifdef JS_ION_PERF |
|
2157 writePerfSpewerJitCodeProfile(code, "ForkJoinGetSliceStub"); |
|
2158 #endif |
|
2159 |
|
2160 return code; |
|
2161 #else |
|
2162 return nullptr; |
|
2163 #endif // JS_THREADSAFE |
|
2164 } |
|
2165 |
|
2166 } // namespace jit |
|
2167 } // namespace js |