Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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/. */
7 #include "jit/x64/CodeGenerator-x64.h"
9 #include "jit/IonCaches.h"
10 #include "jit/MIR.h"
12 #include "jsscriptinlines.h"
14 #include "jit/shared/CodeGenerator-shared-inl.h"
16 using namespace js;
17 using namespace js::jit;
19 CodeGeneratorX64::CodeGeneratorX64(MIRGenerator *gen, LIRGraph *graph, MacroAssembler *masm)
20 : CodeGeneratorX86Shared(gen, graph, masm)
21 {
22 }
24 ValueOperand
25 CodeGeneratorX64::ToValue(LInstruction *ins, size_t pos)
26 {
27 return ValueOperand(ToRegister(ins->getOperand(pos)));
28 }
30 ValueOperand
31 CodeGeneratorX64::ToOutValue(LInstruction *ins)
32 {
33 return ValueOperand(ToRegister(ins->getDef(0)));
34 }
36 ValueOperand
37 CodeGeneratorX64::ToTempValue(LInstruction *ins, size_t pos)
38 {
39 return ValueOperand(ToRegister(ins->getTemp(pos)));
40 }
42 FrameSizeClass
43 FrameSizeClass::FromDepth(uint32_t frameDepth)
44 {
45 return FrameSizeClass::None();
46 }
48 FrameSizeClass
49 FrameSizeClass::ClassLimit()
50 {
51 return FrameSizeClass(0);
52 }
54 uint32_t
55 FrameSizeClass::frameSize() const
56 {
57 MOZ_ASSUME_UNREACHABLE("x64 does not use frame size classes");
58 }
60 bool
61 CodeGeneratorX64::visitValue(LValue *value)
62 {
63 LDefinition *reg = value->getDef(0);
64 masm.moveValue(value->value(), ToRegister(reg));
65 return true;
66 }
68 bool
69 CodeGeneratorX64::visitBox(LBox *box)
70 {
71 const LAllocation *in = box->getOperand(0);
72 const LDefinition *result = box->getDef(0);
74 if (IsFloatingPointType(box->type())) {
75 FloatRegister reg = ToFloatRegister(in);
76 if (box->type() == MIRType_Float32) {
77 masm.convertFloat32ToDouble(reg, ScratchFloatReg);
78 reg = ScratchFloatReg;
79 }
80 masm.movq(reg, ToRegister(result));
81 } else {
82 masm.boxValue(ValueTypeFromMIRType(box->type()), ToRegister(in), ToRegister(result));
83 }
84 return true;
85 }
87 bool
88 CodeGeneratorX64::visitUnbox(LUnbox *unbox)
89 {
90 const ValueOperand value = ToValue(unbox, LUnbox::Input);
91 const LDefinition *result = unbox->output();
92 MUnbox *mir = unbox->mir();
94 if (mir->fallible()) {
95 Assembler::Condition cond;
96 switch (mir->type()) {
97 case MIRType_Int32:
98 cond = masm.testInt32(Assembler::NotEqual, value);
99 break;
100 case MIRType_Boolean:
101 cond = masm.testBoolean(Assembler::NotEqual, value);
102 break;
103 case MIRType_Object:
104 cond = masm.testObject(Assembler::NotEqual, value);
105 break;
106 case MIRType_String:
107 cond = masm.testString(Assembler::NotEqual, value);
108 break;
109 default:
110 MOZ_ASSUME_UNREACHABLE("Given MIRType cannot be unboxed.");
111 }
112 if (!bailoutIf(cond, unbox->snapshot()))
113 return false;
114 }
116 switch (mir->type()) {
117 case MIRType_Int32:
118 masm.unboxInt32(value, ToRegister(result));
119 break;
120 case MIRType_Boolean:
121 masm.unboxBoolean(value, ToRegister(result));
122 break;
123 case MIRType_Object:
124 masm.unboxObject(value, ToRegister(result));
125 break;
126 case MIRType_String:
127 masm.unboxString(value, ToRegister(result));
128 break;
129 default:
130 MOZ_ASSUME_UNREACHABLE("Given MIRType cannot be unboxed.");
131 }
133 return true;
134 }
136 bool
137 CodeGeneratorX64::visitLoadSlotV(LLoadSlotV *load)
138 {
139 ValueOperand dest = ToOutValue(load);
140 Register base = ToRegister(load->input());
141 int32_t offset = load->mir()->slot() * sizeof(js::Value);
143 masm.loadValue(Address(base, offset), dest);
144 return true;
145 }
147 void
148 CodeGeneratorX64::loadUnboxedValue(Operand source, MIRType type, const LDefinition *dest)
149 {
150 switch (type) {
151 case MIRType_Double:
152 masm.loadInt32OrDouble(source, ToFloatRegister(dest));
153 break;
155 case MIRType_Object:
156 case MIRType_String:
157 masm.unboxObject(source, ToRegister(dest));
158 break;
160 case MIRType_Int32:
161 case MIRType_Boolean:
162 masm.movl(source, ToRegister(dest));
163 break;
165 default:
166 MOZ_ASSUME_UNREACHABLE("unexpected type");
167 }
168 }
170 bool
171 CodeGeneratorX64::visitLoadSlotT(LLoadSlotT *load)
172 {
173 Register base = ToRegister(load->input());
174 int32_t offset = load->mir()->slot() * sizeof(js::Value);
176 loadUnboxedValue(Operand(base, offset), load->mir()->type(), load->output());
178 return true;
179 }
181 void
182 CodeGeneratorX64::storeUnboxedValue(const LAllocation *value, MIRType valueType,
183 Operand dest, MIRType slotType)
184 {
185 if (valueType == MIRType_Double) {
186 masm.storeDouble(ToFloatRegister(value), dest);
187 return;
188 }
190 // For known integers and booleans, we can just store the unboxed value if
191 // the slot has the same type.
192 if ((valueType == MIRType_Int32 || valueType == MIRType_Boolean) && slotType == valueType) {
193 if (value->isConstant()) {
194 Value val = *value->toConstant();
195 if (valueType == MIRType_Int32)
196 masm.movl(Imm32(val.toInt32()), dest);
197 else
198 masm.movl(Imm32(val.toBoolean() ? 1 : 0), dest);
199 } else {
200 masm.movl(ToRegister(value), dest);
201 }
202 return;
203 }
205 if (value->isConstant()) {
206 masm.moveValue(*value->toConstant(), ScratchReg);
207 masm.movq(ScratchReg, dest);
208 } else {
209 masm.storeValue(ValueTypeFromMIRType(valueType), ToRegister(value), dest);
210 }
211 }
213 bool
214 CodeGeneratorX64::visitStoreSlotT(LStoreSlotT *store)
215 {
216 Register base = ToRegister(store->slots());
217 int32_t offset = store->mir()->slot() * sizeof(js::Value);
219 const LAllocation *value = store->value();
220 MIRType valueType = store->mir()->value()->type();
221 MIRType slotType = store->mir()->slotType();
223 if (store->mir()->needsBarrier())
224 emitPreBarrier(Address(base, offset), slotType);
226 storeUnboxedValue(value, valueType, Operand(base, offset), slotType);
227 return true;
228 }
230 bool
231 CodeGeneratorX64::visitLoadElementT(LLoadElementT *load)
232 {
233 Operand source = createArrayElementOperand(ToRegister(load->elements()), load->index());
235 if (load->mir()->loadDoubles()) {
236 FloatRegister fpreg = ToFloatRegister(load->output());
237 if (source.kind() == Operand::MEM_REG_DISP)
238 masm.loadDouble(source.toAddress(), fpreg);
239 else
240 masm.loadDouble(source.toBaseIndex(), fpreg);
241 } else {
242 loadUnboxedValue(source, load->mir()->type(), load->output());
243 }
245 JS_ASSERT(!load->mir()->needsHoleCheck());
246 return true;
247 }
250 void
251 CodeGeneratorX64::storeElementTyped(const LAllocation *value, MIRType valueType, MIRType elementType,
252 const Register &elements, const LAllocation *index)
253 {
254 Operand dest = createArrayElementOperand(elements, index);
255 storeUnboxedValue(value, valueType, dest, elementType);
256 }
258 bool
259 CodeGeneratorX64::visitImplicitThis(LImplicitThis *lir)
260 {
261 Register callee = ToRegister(lir->callee());
263 // The implicit |this| is always |undefined| if the function's environment
264 // is the current global.
265 GlobalObject *global = &gen->info().script()->global();
266 masm.cmpPtr(Operand(callee, JSFunction::offsetOfEnvironment()), ImmGCPtr(global));
268 // TODO: OOL stub path.
269 if (!bailoutIf(Assembler::NotEqual, lir->snapshot()))
270 return false;
272 masm.moveValue(UndefinedValue(), ToOutValue(lir));
273 return true;
274 }
276 bool
277 CodeGeneratorX64::visitInterruptCheck(LInterruptCheck *lir)
278 {
279 OutOfLineCode *ool = oolCallVM(InterruptCheckInfo, lir, (ArgList()), StoreNothing());
280 if (!ool)
281 return false;
283 masm.branch32(Assembler::NotEqual,
284 AbsoluteAddress(GetIonContext()->runtime->addressOfInterrupt()), Imm32(0),
285 ool->entry());
286 masm.bind(ool->rejoin());
287 return true;
288 }
290 bool
291 CodeGeneratorX64::visitCompareB(LCompareB *lir)
292 {
293 MCompare *mir = lir->mir();
295 const ValueOperand lhs = ToValue(lir, LCompareB::Lhs);
296 const LAllocation *rhs = lir->rhs();
297 const Register output = ToRegister(lir->output());
299 JS_ASSERT(mir->jsop() == JSOP_STRICTEQ || mir->jsop() == JSOP_STRICTNE);
301 // Load boxed boolean in ScratchReg.
302 if (rhs->isConstant())
303 masm.moveValue(*rhs->toConstant(), ScratchReg);
304 else
305 masm.boxValue(JSVAL_TYPE_BOOLEAN, ToRegister(rhs), ScratchReg);
307 // Perform the comparison.
308 masm.cmpq(lhs.valueReg(), ScratchReg);
309 masm.emitSet(JSOpToCondition(mir->compareType(), mir->jsop()), output);
310 return true;
311 }
313 bool
314 CodeGeneratorX64::visitCompareBAndBranch(LCompareBAndBranch *lir)
315 {
316 MCompare *mir = lir->cmpMir();
318 const ValueOperand lhs = ToValue(lir, LCompareBAndBranch::Lhs);
319 const LAllocation *rhs = lir->rhs();
321 JS_ASSERT(mir->jsop() == JSOP_STRICTEQ || mir->jsop() == JSOP_STRICTNE);
323 // Load boxed boolean in ScratchReg.
324 if (rhs->isConstant())
325 masm.moveValue(*rhs->toConstant(), ScratchReg);
326 else
327 masm.boxValue(JSVAL_TYPE_BOOLEAN, ToRegister(rhs), ScratchReg);
329 // Perform the comparison.
330 masm.cmpq(lhs.valueReg(), ScratchReg);
331 emitBranch(JSOpToCondition(mir->compareType(), mir->jsop()), lir->ifTrue(), lir->ifFalse());
332 return true;
333 }
334 bool
335 CodeGeneratorX64::visitCompareV(LCompareV *lir)
336 {
337 MCompare *mir = lir->mir();
338 const ValueOperand lhs = ToValue(lir, LCompareV::LhsInput);
339 const ValueOperand rhs = ToValue(lir, LCompareV::RhsInput);
340 const Register output = ToRegister(lir->output());
342 JS_ASSERT(IsEqualityOp(mir->jsop()));
344 masm.cmpq(lhs.valueReg(), rhs.valueReg());
345 masm.emitSet(JSOpToCondition(mir->compareType(), mir->jsop()), output);
346 return true;
347 }
349 bool
350 CodeGeneratorX64::visitCompareVAndBranch(LCompareVAndBranch *lir)
351 {
352 MCompare *mir = lir->cmpMir();
354 const ValueOperand lhs = ToValue(lir, LCompareVAndBranch::LhsInput);
355 const ValueOperand rhs = ToValue(lir, LCompareVAndBranch::RhsInput);
357 JS_ASSERT(mir->jsop() == JSOP_EQ || mir->jsop() == JSOP_STRICTEQ ||
358 mir->jsop() == JSOP_NE || mir->jsop() == JSOP_STRICTNE);
360 masm.cmpq(lhs.valueReg(), rhs.valueReg());
361 emitBranch(JSOpToCondition(mir->compareType(), mir->jsop()), lir->ifTrue(), lir->ifFalse());
362 return true;
363 }
365 bool
366 CodeGeneratorX64::visitAsmJSUInt32ToDouble(LAsmJSUInt32ToDouble *lir)
367 {
368 masm.convertUInt32ToDouble(ToRegister(lir->input()), ToFloatRegister(lir->output()));
369 return true;
370 }
372 bool
373 CodeGeneratorX64::visitAsmJSUInt32ToFloat32(LAsmJSUInt32ToFloat32 *lir)
374 {
375 masm.convertUInt32ToFloat32(ToRegister(lir->input()), ToFloatRegister(lir->output()));
376 return true;
377 }
379 bool
380 CodeGeneratorX64::visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic *ins)
381 {
382 MOZ_ASSUME_UNREACHABLE("NYI");
383 }
385 bool
386 CodeGeneratorX64::visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStatic *ins)
387 {
388 MOZ_ASSUME_UNREACHABLE("NYI");
389 }
391 bool
392 CodeGeneratorX64::visitAsmJSLoadHeap(LAsmJSLoadHeap *ins)
393 {
394 MAsmJSLoadHeap *mir = ins->mir();
395 ArrayBufferView::ViewType vt = mir->viewType();
396 const LAllocation *ptr = ins->ptr();
398 // No need to note the access if it will never fault.
399 bool skipNote = mir->skipBoundsCheck();
400 Operand srcAddr(HeapReg);
402 if (ptr->isConstant()) {
403 int32_t ptrImm = ptr->toConstant()->toInt32();
404 // Note only a positive index is accepted here because a negative offset would
405 // not wrap back into the protected area reserved for the heap.
406 JS_ASSERT(ptrImm >= 0);
407 srcAddr = Operand(HeapReg, ptrImm);
408 } else {
409 srcAddr = Operand(HeapReg, ToRegister(ptr), TimesOne);
410 }
412 uint32_t before = masm.size();
413 switch (vt) {
414 case ArrayBufferView::TYPE_INT8: masm.movsbl(srcAddr, ToRegister(ins->output())); break;
415 case ArrayBufferView::TYPE_UINT8: masm.movzbl(srcAddr, ToRegister(ins->output())); break;
416 case ArrayBufferView::TYPE_INT16: masm.movswl(srcAddr, ToRegister(ins->output())); break;
417 case ArrayBufferView::TYPE_UINT16: masm.movzwl(srcAddr, ToRegister(ins->output())); break;
418 case ArrayBufferView::TYPE_INT32:
419 case ArrayBufferView::TYPE_UINT32: masm.movl(srcAddr, ToRegister(ins->output())); break;
420 case ArrayBufferView::TYPE_FLOAT32: masm.loadFloat32(srcAddr, ToFloatRegister(ins->output())); break;
421 case ArrayBufferView::TYPE_FLOAT64: masm.loadDouble(srcAddr, ToFloatRegister(ins->output())); break;
422 default: MOZ_ASSUME_UNREACHABLE("unexpected array type");
423 }
424 uint32_t after = masm.size();
425 return skipNote || masm.append(AsmJSHeapAccess(before, after, vt, ToAnyRegister(ins->output())));
426 }
428 bool
429 CodeGeneratorX64::visitAsmJSStoreHeap(LAsmJSStoreHeap *ins)
430 {
431 MAsmJSStoreHeap *mir = ins->mir();
432 ArrayBufferView::ViewType vt = mir->viewType();
433 const LAllocation *ptr = ins->ptr();
434 // No need to note the access if it will never fault.
435 bool skipNote = mir->skipBoundsCheck();
436 Operand dstAddr(HeapReg);
438 if (ptr->isConstant()) {
439 int32_t ptrImm = ptr->toConstant()->toInt32();
440 // Note only a positive index is accepted here because a negative offset would
441 // not wrap back into the protected area reserved for the heap.
442 JS_ASSERT(ptrImm >= 0);
443 dstAddr = Operand(HeapReg, ptrImm);
444 } else {
445 dstAddr = Operand(HeapReg, ToRegister(ins->ptr()), TimesOne);
446 }
448 uint32_t before = masm.size();
449 if (ins->value()->isConstant()) {
450 switch (vt) {
451 case ArrayBufferView::TYPE_INT8:
452 case ArrayBufferView::TYPE_UINT8: masm.movb(Imm32(ToInt32(ins->value())), dstAddr); break;
453 case ArrayBufferView::TYPE_INT16:
454 case ArrayBufferView::TYPE_UINT16: masm.movw(Imm32(ToInt32(ins->value())), dstAddr); break;
455 case ArrayBufferView::TYPE_INT32:
456 case ArrayBufferView::TYPE_UINT32: masm.movl(Imm32(ToInt32(ins->value())), dstAddr); break;
457 default: MOZ_ASSUME_UNREACHABLE("unexpected array type");
458 }
459 } else {
460 switch (vt) {
461 case ArrayBufferView::TYPE_INT8:
462 case ArrayBufferView::TYPE_UINT8: masm.movb(ToRegister(ins->value()), dstAddr); break;
463 case ArrayBufferView::TYPE_INT16:
464 case ArrayBufferView::TYPE_UINT16: masm.movw(ToRegister(ins->value()), dstAddr); break;
465 case ArrayBufferView::TYPE_INT32:
466 case ArrayBufferView::TYPE_UINT32: masm.movl(ToRegister(ins->value()), dstAddr); break;
467 case ArrayBufferView::TYPE_FLOAT32: masm.storeFloat32(ToFloatRegister(ins->value()), dstAddr); break;
468 case ArrayBufferView::TYPE_FLOAT64: masm.storeDouble(ToFloatRegister(ins->value()), dstAddr); break;
469 default: MOZ_ASSUME_UNREACHABLE("unexpected array type");
470 }
471 }
472 uint32_t after = masm.size();
473 return skipNote || masm.append(AsmJSHeapAccess(before, after));
474 }
476 bool
477 CodeGeneratorX64::visitAsmJSLoadGlobalVar(LAsmJSLoadGlobalVar *ins)
478 {
479 MAsmJSLoadGlobalVar *mir = ins->mir();
481 CodeOffsetLabel label;
482 if (mir->type() == MIRType_Int32)
483 label = masm.loadRipRelativeInt32(ToRegister(ins->output()));
484 else
485 label = masm.loadRipRelativeDouble(ToFloatRegister(ins->output()));
487 return masm.append(AsmJSGlobalAccess(label.offset(), mir->globalDataOffset()));
488 }
490 bool
491 CodeGeneratorX64::visitAsmJSStoreGlobalVar(LAsmJSStoreGlobalVar *ins)
492 {
493 MAsmJSStoreGlobalVar *mir = ins->mir();
495 MIRType type = mir->value()->type();
496 JS_ASSERT(IsNumberType(type));
498 CodeOffsetLabel label;
499 if (type == MIRType_Int32)
500 label = masm.storeRipRelativeInt32(ToRegister(ins->value()));
501 else
502 label = masm.storeRipRelativeDouble(ToFloatRegister(ins->value()));
504 return masm.append(AsmJSGlobalAccess(label.offset(), mir->globalDataOffset()));
505 }
507 bool
508 CodeGeneratorX64::visitAsmJSLoadFuncPtr(LAsmJSLoadFuncPtr *ins)
509 {
510 MAsmJSLoadFuncPtr *mir = ins->mir();
512 Register index = ToRegister(ins->index());
513 Register tmp = ToRegister(ins->temp());
514 Register out = ToRegister(ins->output());
516 CodeOffsetLabel label = masm.leaRipRelative(tmp);
517 masm.loadPtr(Operand(tmp, index, TimesEight, 0), out);
519 return masm.append(AsmJSGlobalAccess(label.offset(), mir->globalDataOffset()));
520 }
522 bool
523 CodeGeneratorX64::visitAsmJSLoadFFIFunc(LAsmJSLoadFFIFunc *ins)
524 {
525 MAsmJSLoadFFIFunc *mir = ins->mir();
527 CodeOffsetLabel label = masm.loadRipRelativeInt64(ToRegister(ins->output()));
529 return masm.append(AsmJSGlobalAccess(label.offset(), mir->globalDataOffset()));
530 }
532 void
533 DispatchIonCache::initializeAddCacheState(LInstruction *ins, AddCacheState *addState)
534 {
535 // Can always use the scratch register on x64.
536 addState->dispatchScratch = ScratchReg;
537 }
539 bool
540 CodeGeneratorX64::visitTruncateDToInt32(LTruncateDToInt32 *ins)
541 {
542 FloatRegister input = ToFloatRegister(ins->input());
543 Register output = ToRegister(ins->output());
545 // On x64, branchTruncateDouble uses cvttsd2sq. Unlike the x86
546 // implementation, this should handle most doubles and we can just
547 // call a stub if it fails.
548 return emitTruncateDouble(input, output);
549 }
551 bool
552 CodeGeneratorX64::visitTruncateFToInt32(LTruncateFToInt32 *ins)
553 {
554 FloatRegister input = ToFloatRegister(ins->input());
555 Register output = ToRegister(ins->output());
557 // On x64, branchTruncateFloat32 uses cvttss2sq. Unlike the x86
558 // implementation, this should handle most floats and we can just
559 // call a stub if it fails.
560 return emitTruncateFloat32(input, output);
561 }