Line data Source code
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/x64/Lowering-x64.h"
8 :
9 : #include "jit/MIR.h"
10 : #include "jit/x64/Assembler-x64.h"
11 :
12 : #include "jit/shared/Lowering-shared-inl.h"
13 :
14 : using namespace js;
15 : using namespace js::jit;
16 :
17 : LBoxAllocation
18 0 : LIRGeneratorX64::useBoxFixed(MDefinition* mir, Register reg1, Register, bool useAtStart)
19 : {
20 0 : MOZ_ASSERT(mir->type() == MIRType::Value);
21 :
22 0 : ensureDefined(mir);
23 0 : return LBoxAllocation(LUse(reg1, mir->virtualRegister(), useAtStart));
24 : }
25 :
26 : LAllocation
27 0 : LIRGeneratorX64::useByteOpRegister(MDefinition* mir)
28 : {
29 0 : return useRegister(mir);
30 : }
31 :
32 : LAllocation
33 0 : LIRGeneratorX64::useByteOpRegisterAtStart(MDefinition* mir)
34 : {
35 0 : return useRegisterAtStart(mir);
36 : }
37 :
38 : LAllocation
39 0 : LIRGeneratorX64::useByteOpRegisterOrNonDoubleConstant(MDefinition* mir)
40 : {
41 0 : return useRegisterOrNonDoubleConstant(mir);
42 : }
43 :
44 : LDefinition
45 0 : LIRGeneratorX64::tempByteOpRegister()
46 : {
47 0 : return temp();
48 : }
49 :
50 : LDefinition
51 51 : LIRGeneratorX64::tempToUnbox()
52 : {
53 51 : return temp();
54 : }
55 :
56 : void
57 0 : LIRGeneratorX64::lowerForALUInt64(LInstructionHelper<INT64_PIECES, 2 * INT64_PIECES, 0>* ins,
58 : MDefinition* mir, MDefinition* lhs, MDefinition* rhs)
59 : {
60 0 : ins->setInt64Operand(0, useInt64RegisterAtStart(lhs));
61 : ins->setInt64Operand(INT64_PIECES,
62 0 : lhs != rhs ? useInt64OrConstant(rhs) : useInt64OrConstantAtStart(rhs));
63 0 : defineInt64ReuseInput(ins, mir, 0);
64 0 : }
65 :
66 : void
67 0 : LIRGeneratorX64::lowerForMulInt64(LMulI64* ins, MMul* mir, MDefinition* lhs, MDefinition* rhs)
68 : {
69 : // X64 doesn't need a temp for 64bit multiplication.
70 0 : ins->setInt64Operand(0, useInt64RegisterAtStart(lhs));
71 0 : ins->setInt64Operand(INT64_PIECES,
72 0 : lhs != rhs ? useInt64OrConstant(rhs) : useInt64OrConstantAtStart(rhs));
73 0 : defineInt64ReuseInput(ins, mir, 0);
74 0 : }
75 :
76 : void
77 117 : LIRGeneratorX64::visitBox(MBox* box)
78 : {
79 117 : MDefinition* opd = box->getOperand(0);
80 :
81 : // If the operand is a constant, emit near its uses.
82 117 : if (opd->isConstant() && box->canEmitAtUses()) {
83 30 : emitAtUses(box);
84 30 : return;
85 : }
86 :
87 87 : if (opd->isConstant()) {
88 53 : define(new(alloc()) LValue(opd->toConstant()->toJSValue()), box, LDefinition(LDefinition::BOX));
89 : } else {
90 34 : LBox* ins = new(alloc()) LBox(useRegister(opd), opd->type());
91 34 : define(ins, box, LDefinition(LDefinition::BOX));
92 : }
93 : }
94 :
95 : void
96 109 : LIRGeneratorX64::visitUnbox(MUnbox* unbox)
97 : {
98 109 : MDefinition* box = unbox->getOperand(0);
99 :
100 109 : if (box->type() == MIRType::ObjectOrNull) {
101 0 : LUnboxObjectOrNull* lir = new(alloc()) LUnboxObjectOrNull(useRegisterAtStart(box));
102 0 : if (unbox->fallible())
103 0 : assignSnapshot(lir, unbox->bailoutKind());
104 0 : defineReuseInput(lir, unbox, 0);
105 0 : return;
106 : }
107 :
108 109 : MOZ_ASSERT(box->type() == MIRType::Value);
109 :
110 : LUnboxBase* lir;
111 109 : if (IsFloatingPointType(unbox->type())) {
112 0 : lir = new(alloc()) LUnboxFloatingPoint(useRegisterAtStart(box), unbox->type());
113 109 : } else if (unbox->fallible()) {
114 : // If the unbox is fallible, load the Value in a register first to
115 : // avoid multiple loads.
116 80 : lir = new(alloc()) LUnbox(useRegisterAtStart(box));
117 : } else {
118 29 : lir = new(alloc()) LUnbox(useAtStart(box));
119 : }
120 :
121 109 : if (unbox->fallible())
122 80 : assignSnapshot(lir, unbox->bailoutKind());
123 :
124 109 : define(lir, unbox);
125 : }
126 :
127 : void
128 17 : LIRGeneratorX64::visitReturn(MReturn* ret)
129 : {
130 17 : MDefinition* opd = ret->getOperand(0);
131 17 : MOZ_ASSERT(opd->type() == MIRType::Value);
132 :
133 17 : LReturn* ins = new(alloc()) LReturn;
134 17 : ins->setOperand(0, useFixed(opd, JSReturnReg));
135 17 : add(ins);
136 17 : }
137 :
138 : void
139 86 : LIRGeneratorX64::defineUntypedPhi(MPhi* phi, size_t lirIndex)
140 : {
141 86 : defineTypedPhi(phi, lirIndex);
142 86 : }
143 :
144 : void
145 222 : LIRGeneratorX64::lowerUntypedPhiInput(MPhi* phi, uint32_t inputPosition, LBlock* block, size_t lirIndex)
146 : {
147 222 : lowerTypedPhiInput(phi, inputPosition, block, lirIndex);
148 222 : }
149 :
150 : void
151 0 : LIRGeneratorX64::defineInt64Phi(MPhi* phi, size_t lirIndex)
152 : {
153 0 : defineTypedPhi(phi, lirIndex);
154 0 : }
155 :
156 : void
157 0 : LIRGeneratorX64::lowerInt64PhiInput(MPhi* phi, uint32_t inputPosition, LBlock* block, size_t lirIndex)
158 : {
159 0 : lowerTypedPhiInput(phi, inputPosition, block, lirIndex);
160 0 : }
161 :
162 : void
163 0 : LIRGeneratorX64::visitCompareExchangeTypedArrayElement(MCompareExchangeTypedArrayElement* ins)
164 : {
165 0 : lowerCompareExchangeTypedArrayElement(ins, /* useI386ByteRegisters = */ false);
166 0 : }
167 :
168 : void
169 0 : LIRGeneratorX64::visitAtomicExchangeTypedArrayElement(MAtomicExchangeTypedArrayElement* ins)
170 : {
171 0 : lowerAtomicExchangeTypedArrayElement(ins, /* useI386ByteRegisters = */ false);
172 0 : }
173 :
174 : void
175 0 : LIRGeneratorX64::visitAtomicTypedArrayElementBinop(MAtomicTypedArrayElementBinop* ins)
176 : {
177 0 : lowerAtomicTypedArrayElementBinop(ins, /* useI386ByteRegisters = */ false);
178 0 : }
179 :
180 : void
181 0 : LIRGeneratorX64::visitWasmUnsignedToDouble(MWasmUnsignedToDouble* ins)
182 : {
183 0 : MOZ_ASSERT(ins->input()->type() == MIRType::Int32);
184 0 : LWasmUint32ToDouble* lir = new(alloc()) LWasmUint32ToDouble(useRegisterAtStart(ins->input()));
185 0 : define(lir, ins);
186 0 : }
187 :
188 : void
189 0 : LIRGeneratorX64::visitWasmUnsignedToFloat32(MWasmUnsignedToFloat32* ins)
190 : {
191 0 : MOZ_ASSERT(ins->input()->type() == MIRType::Int32);
192 0 : LWasmUint32ToFloat32* lir = new(alloc()) LWasmUint32ToFloat32(useRegisterAtStart(ins->input()));
193 0 : define(lir, ins);
194 0 : }
195 :
196 : void
197 0 : LIRGeneratorX64::visitWasmLoad(MWasmLoad* ins)
198 : {
199 0 : MDefinition* base = ins->base();
200 0 : MOZ_ASSERT(base->type() == MIRType::Int32);
201 :
202 0 : if (ins->type() != MIRType::Int64) {
203 0 : auto* lir = new(alloc()) LWasmLoad(useRegisterOrZeroAtStart(base));
204 0 : define(lir, ins);
205 0 : return;
206 : }
207 :
208 0 : auto* lir = new(alloc()) LWasmLoadI64(useRegisterOrZeroAtStart(base));
209 0 : defineInt64(lir, ins);
210 : }
211 :
212 : void
213 0 : LIRGeneratorX64::visitWasmStore(MWasmStore* ins)
214 : {
215 0 : MDefinition* base = ins->base();
216 0 : MOZ_ASSERT(base->type() == MIRType::Int32);
217 :
218 0 : MDefinition* value = ins->value();
219 0 : LAllocation valueAlloc;
220 0 : switch (ins->access().type()) {
221 : case Scalar::Int8:
222 : case Scalar::Uint8:
223 : case Scalar::Int16:
224 : case Scalar::Uint16:
225 : case Scalar::Int32:
226 : case Scalar::Uint32:
227 0 : valueAlloc = useRegisterOrConstantAtStart(value);
228 0 : break;
229 : case Scalar::Int64:
230 : // No way to encode an int64-to-memory move on x64.
231 0 : if (value->isConstant() && value->type() != MIRType::Int64)
232 0 : valueAlloc = useOrConstantAtStart(value);
233 : else
234 0 : valueAlloc = useRegisterAtStart(value);
235 0 : break;
236 : case Scalar::Float32:
237 : case Scalar::Float64:
238 : case Scalar::Float32x4:
239 : case Scalar::Int8x16:
240 : case Scalar::Int16x8:
241 : case Scalar::Int32x4:
242 0 : valueAlloc = useRegisterAtStart(value);
243 0 : break;
244 : case Scalar::Uint8Clamped:
245 : case Scalar::MaxTypedArrayViewType:
246 0 : MOZ_CRASH("unexpected array type");
247 : }
248 :
249 0 : LAllocation baseAlloc = useRegisterOrZeroAtStart(base);
250 0 : auto* lir = new(alloc()) LWasmStore(baseAlloc, valueAlloc);
251 0 : add(lir, ins);
252 0 : }
253 :
254 : void
255 0 : LIRGeneratorX64::visitAsmJSLoadHeap(MAsmJSLoadHeap* ins)
256 : {
257 0 : MDefinition* base = ins->base();
258 0 : MOZ_ASSERT(base->type() == MIRType::Int32);
259 :
260 0 : define(new(alloc()) LAsmJSLoadHeap(useRegisterOrZeroAtStart(base)), ins);
261 0 : }
262 :
263 : void
264 0 : LIRGeneratorX64::visitAsmJSStoreHeap(MAsmJSStoreHeap* ins)
265 : {
266 0 : MDefinition* base = ins->base();
267 0 : MOZ_ASSERT(base->type() == MIRType::Int32);
268 :
269 0 : LAsmJSStoreHeap* lir = nullptr; // initialize to silence GCC warning
270 0 : switch (ins->access().type()) {
271 : case Scalar::Int8:
272 : case Scalar::Uint8:
273 : case Scalar::Int16:
274 : case Scalar::Uint16:
275 : case Scalar::Int32:
276 : case Scalar::Uint32:
277 0 : lir = new(alloc()) LAsmJSStoreHeap(useRegisterOrZeroAtStart(base),
278 0 : useRegisterOrConstantAtStart(ins->value()));
279 0 : break;
280 : case Scalar::Float32:
281 : case Scalar::Float64:
282 : case Scalar::Float32x4:
283 : case Scalar::Int8x16:
284 : case Scalar::Int16x8:
285 : case Scalar::Int32x4:
286 0 : lir = new(alloc()) LAsmJSStoreHeap(useRegisterOrZeroAtStart(base),
287 0 : useRegisterAtStart(ins->value()));
288 0 : break;
289 : case Scalar::Int64:
290 : case Scalar::Uint8Clamped:
291 : case Scalar::MaxTypedArrayViewType:
292 0 : MOZ_CRASH("unexpected array type");
293 : }
294 0 : add(lir, ins);
295 0 : }
296 :
297 : void
298 0 : LIRGeneratorX64::visitAsmJSCompareExchangeHeap(MAsmJSCompareExchangeHeap* ins)
299 : {
300 0 : MDefinition* base = ins->base();
301 0 : MOZ_ASSERT(base->type() == MIRType::Int32);
302 :
303 : // The output may not be used but will be clobbered regardless, so
304 : // pin the output to eax.
305 : //
306 : // The input values must both be in registers.
307 :
308 0 : const LAllocation oldval = useRegister(ins->oldValue());
309 0 : const LAllocation newval = useRegister(ins->newValue());
310 :
311 : LAsmJSCompareExchangeHeap* lir =
312 0 : new(alloc()) LAsmJSCompareExchangeHeap(useRegister(base), oldval, newval);
313 :
314 0 : defineFixed(lir, ins, LAllocation(AnyRegister(eax)));
315 0 : }
316 :
317 : void
318 0 : LIRGeneratorX64::visitAsmJSAtomicExchangeHeap(MAsmJSAtomicExchangeHeap* ins)
319 : {
320 0 : MOZ_ASSERT(ins->base()->type() == MIRType::Int32);
321 :
322 0 : const LAllocation base = useRegister(ins->base());
323 0 : const LAllocation value = useRegister(ins->value());
324 :
325 : // The output may not be used but will be clobbered regardless,
326 : // so ignore the case where we're not using the value and just
327 : // use the output register as a temp.
328 :
329 : LAsmJSAtomicExchangeHeap* lir =
330 0 : new(alloc()) LAsmJSAtomicExchangeHeap(base, value);
331 0 : define(lir, ins);
332 0 : }
333 :
334 : void
335 0 : LIRGeneratorX64::visitAsmJSAtomicBinopHeap(MAsmJSAtomicBinopHeap* ins)
336 : {
337 0 : MDefinition* base = ins->base();
338 0 : MOZ_ASSERT(base->type() == MIRType::Int32);
339 :
340 : // Case 1: the result of the operation is not used.
341 : //
342 : // We'll emit a single instruction: LOCK ADD, LOCK SUB, LOCK AND,
343 : // LOCK OR, or LOCK XOR.
344 :
345 0 : if (!ins->hasUses()) {
346 : LAsmJSAtomicBinopHeapForEffect* lir =
347 0 : new(alloc()) LAsmJSAtomicBinopHeapForEffect(useRegister(base),
348 0 : useRegisterOrConstant(ins->value()));
349 0 : add(lir, ins);
350 0 : return;
351 : }
352 :
353 : // Case 2: the result of the operation is used.
354 : //
355 : // For ADD and SUB we'll use XADD with word and byte ops as
356 : // appropriate. Any output register can be used and if value is a
357 : // register it's best if it's the same as output:
358 : //
359 : // movl value, output ; if value != output
360 : // lock xaddl output, mem
361 : //
362 : // For AND/OR/XOR we need to use a CMPXCHG loop, and the output is
363 : // always in rax:
364 : //
365 : // movl *mem, rax
366 : // L: mov rax, temp
367 : // andl value, temp
368 : // lock cmpxchg temp, mem ; reads rax also
369 : // jnz L
370 : // ; result in rax
371 : //
372 : // Note the placement of L, cmpxchg will update rax with *mem if
373 : // *mem does not have the expected value, so reloading it at the
374 : // top of the loop would be redundant.
375 :
376 0 : bool bitOp = !(ins->operation() == AtomicFetchAddOp || ins->operation() == AtomicFetchSubOp);
377 0 : bool reuseInput = false;
378 0 : LAllocation value;
379 :
380 0 : if (bitOp || ins->value()->isConstant()) {
381 0 : value = useRegisterOrConstant(ins->value());
382 : } else {
383 0 : reuseInput = true;
384 0 : value = useRegisterAtStart(ins->value());
385 : }
386 :
387 : LAsmJSAtomicBinopHeap* lir =
388 0 : new(alloc()) LAsmJSAtomicBinopHeap(useRegister(base),
389 : value,
390 0 : bitOp ? temp() : LDefinition::BogusTemp());
391 :
392 0 : if (reuseInput)
393 0 : defineReuseInput(lir, ins, LAsmJSAtomicBinopHeap::valueOp);
394 0 : else if (bitOp)
395 0 : defineFixed(lir, ins, LAllocation(AnyRegister(rax)));
396 : else
397 0 : define(lir, ins);
398 : }
399 :
400 : void
401 0 : LIRGeneratorX64::visitSubstr(MSubstr* ins)
402 : {
403 0 : LSubstr* lir = new (alloc()) LSubstr(useRegister(ins->string()),
404 0 : useRegister(ins->begin()),
405 0 : useRegister(ins->length()),
406 0 : temp(),
407 0 : temp(),
408 0 : tempByteOpRegister());
409 0 : define(lir, ins);
410 0 : assignSafepoint(lir, ins);
411 0 : }
412 :
413 : void
414 0 : LIRGeneratorX64::visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic* ins)
415 : {
416 0 : MOZ_CRASH("NYI");
417 : }
418 :
419 : void
420 0 : LIRGeneratorX64::visitRandom(MRandom* ins)
421 : {
422 0 : LRandom *lir = new(alloc()) LRandom(temp(),
423 0 : temp(),
424 0 : temp());
425 0 : defineFixed(lir, ins, LFloatReg(ReturnDoubleReg));
426 0 : }
427 :
428 : void
429 0 : LIRGeneratorX64::lowerDivI64(MDiv* div)
430 : {
431 0 : if (div->isUnsigned()) {
432 0 : lowerUDivI64(div);
433 0 : return;
434 : }
435 :
436 0 : LDivOrModI64* lir = new(alloc()) LDivOrModI64(useRegister(div->lhs()), useRegister(div->rhs()),
437 0 : tempFixed(rdx));
438 0 : defineInt64Fixed(lir, div, LInt64Allocation(LAllocation(AnyRegister(rax))));
439 : }
440 :
441 : void
442 0 : LIRGeneratorX64::lowerModI64(MMod* mod)
443 : {
444 0 : if (mod->isUnsigned()) {
445 0 : lowerUModI64(mod);
446 0 : return;
447 : }
448 :
449 0 : LDivOrModI64* lir = new(alloc()) LDivOrModI64(useRegister(mod->lhs()), useRegister(mod->rhs()),
450 0 : tempFixed(rax));
451 0 : defineInt64Fixed(lir, mod, LInt64Allocation(LAllocation(AnyRegister(rdx))));
452 : }
453 :
454 : void
455 0 : LIRGeneratorX64::lowerUDivI64(MDiv* div)
456 : {
457 0 : LUDivOrModI64* lir = new(alloc()) LUDivOrModI64(useRegister(div->lhs()),
458 0 : useRegister(div->rhs()),
459 0 : tempFixed(rdx));
460 0 : defineInt64Fixed(lir, div, LInt64Allocation(LAllocation(AnyRegister(rax))));
461 0 : }
462 :
463 : void
464 0 : LIRGeneratorX64::lowerUModI64(MMod* mod)
465 : {
466 0 : LUDivOrModI64* lir = new(alloc()) LUDivOrModI64(useRegister(mod->lhs()),
467 0 : useRegister(mod->rhs()),
468 0 : tempFixed(rax));
469 0 : defineInt64Fixed(lir, mod, LInt64Allocation(LAllocation(AnyRegister(rdx))));
470 0 : }
471 :
472 : void
473 0 : LIRGeneratorX64::visitWasmTruncateToInt64(MWasmTruncateToInt64* ins)
474 : {
475 0 : MDefinition* opd = ins->input();
476 0 : MOZ_ASSERT(opd->type() == MIRType::Double || opd->type() == MIRType::Float32);
477 :
478 0 : LDefinition maybeTemp = ins->isUnsigned() ? tempDouble() : LDefinition::BogusTemp();
479 0 : defineInt64(new(alloc()) LWasmTruncateToInt64(useRegister(opd), maybeTemp), ins);
480 0 : }
481 :
482 : void
483 0 : LIRGeneratorX64::visitInt64ToFloatingPoint(MInt64ToFloatingPoint* ins)
484 : {
485 0 : MDefinition* opd = ins->input();
486 0 : MOZ_ASSERT(opd->type() == MIRType::Int64);
487 0 : MOZ_ASSERT(IsFloatingPointType(ins->type()));
488 :
489 0 : LDefinition maybeTemp = ins->isUnsigned() ? temp() : LDefinition::BogusTemp();
490 0 : define(new(alloc()) LInt64ToFloatingPoint(useInt64Register(opd), maybeTemp), ins);
491 0 : }
492 :
493 : void
494 0 : LIRGeneratorX64::visitExtendInt32ToInt64(MExtendInt32ToInt64* ins)
495 : {
496 0 : defineInt64(new(alloc()) LExtendInt32ToInt64(useAtStart(ins->input())), ins);
497 0 : }
|