Line data Source code
1 : // Copyright 2017 the V8 project authors. All rights reserved.
2 : // Use of this source code is governed by a BSD-style license that can be
3 : // found in the LICENSE file.
4 :
5 : #ifndef V8_WASM_BASELINE_X64_LIFTOFF_ASSEMBLER_X64_H_
6 : #define V8_WASM_BASELINE_X64_LIFTOFF_ASSEMBLER_X64_H_
7 :
8 : #include "src/wasm/baseline/liftoff-assembler.h"
9 :
10 : #include "src/assembler.h"
11 : #include "src/wasm/value-type.h"
12 :
13 : namespace v8 {
14 : namespace internal {
15 : namespace wasm {
16 :
17 : #define REQUIRE_CPU_FEATURE(name, ...) \
18 : if (!CpuFeatures::IsSupported(name)) { \
19 : bailout("no " #name); \
20 : return __VA_ARGS__; \
21 : } \
22 : CpuFeatureScope feature(this, name);
23 :
24 : namespace liftoff {
25 :
26 : constexpr Register kScratchRegister2 = r11;
27 : static_assert(kScratchRegister != kScratchRegister2, "collision");
28 : static_assert((kLiftoffAssemblerGpCacheRegs &
29 : Register::ListOf<kScratchRegister, kScratchRegister2>()) == 0,
30 : "scratch registers must not be used as cache registers");
31 :
32 : constexpr DoubleRegister kScratchDoubleReg2 = xmm14;
33 : static_assert(kScratchDoubleReg != kScratchDoubleReg2, "collision");
34 : static_assert(
35 : (kLiftoffAssemblerFpCacheRegs &
36 : DoubleRegister::ListOf<kScratchDoubleReg, kScratchDoubleReg2>()) == 0,
37 : "scratch registers must not be used as cache registers");
38 :
39 : // rbp-8 holds the stack marker, rbp-16 is the instance parameter, first stack
40 : // slot is located at rbp-24.
41 : constexpr int32_t kConstantStackSpace = 16;
42 : constexpr int32_t kFirstStackSlotOffset =
43 : kConstantStackSpace + LiftoffAssembler::kStackSlotSize;
44 :
45 706704 : inline Operand GetStackSlot(uint32_t index) {
46 706704 : int32_t offset = index * LiftoffAssembler::kStackSlotSize;
47 706704 : return Operand(rbp, -kFirstStackSlotOffset - offset);
48 : }
49 :
50 : // TODO(clemensh): Make this a constexpr variable once Operand is constexpr.
51 1489503 : inline Operand GetInstanceOperand() { return Operand(rbp, -16); }
52 :
53 502673 : inline Operand GetMemOp(LiftoffAssembler* assm, Register addr, Register offset,
54 : uint32_t offset_imm) {
55 502673 : if (is_uint31(offset_imm)) {
56 502673 : if (offset == no_reg) return Operand(addr, offset_imm);
57 246400 : return Operand(addr, offset, times_1, offset_imm);
58 : }
59 : // Offset immediate does not fit in 31 bits.
60 : Register scratch = kScratchRegister;
61 0 : assm->movl(scratch, Immediate(offset_imm));
62 0 : if (offset != no_reg) {
63 0 : assm->addq(scratch, offset);
64 : }
65 0 : return Operand(addr, scratch, times_1, 0);
66 : }
67 :
68 36721 : inline void Load(LiftoffAssembler* assm, LiftoffRegister dst, Operand src,
69 : ValueType type) {
70 36721 : switch (type) {
71 : case kWasmI32:
72 10998 : assm->movl(dst.gp(), src);
73 : break;
74 : case kWasmI64:
75 4262 : assm->movq(dst.gp(), src);
76 : break;
77 : case kWasmF32:
78 : assm->Movss(dst.fp(), src);
79 : break;
80 : case kWasmF64:
81 : assm->Movsd(dst.fp(), src);
82 : break;
83 : default:
84 0 : UNREACHABLE();
85 : }
86 36721 : }
87 :
88 67372 : inline void Store(LiftoffAssembler* assm, Operand dst, LiftoffRegister src,
89 : ValueType type) {
90 67372 : switch (type) {
91 : case kWasmI32:
92 67372 : assm->movl(dst, src.gp());
93 : break;
94 : case kWasmI64:
95 0 : assm->movq(dst, src.gp());
96 : break;
97 : case kWasmF32:
98 : assm->Movss(dst, src.fp());
99 : break;
100 : case kWasmF64:
101 : assm->Movsd(dst, src.fp());
102 : break;
103 : default:
104 0 : UNREACHABLE();
105 : }
106 67372 : }
107 :
108 15140 : inline void push(LiftoffAssembler* assm, LiftoffRegister reg, ValueType type) {
109 15140 : switch (type) {
110 : case kWasmI32:
111 : case kWasmI64:
112 4542 : assm->pushq(reg.gp());
113 4542 : break;
114 : case kWasmF32:
115 4811 : assm->subp(rsp, Immediate(kSystemPointerSize));
116 9622 : assm->Movss(Operand(rsp, 0), reg.fp());
117 4811 : break;
118 : case kWasmF64:
119 5787 : assm->subp(rsp, Immediate(kSystemPointerSize));
120 11574 : assm->Movsd(Operand(rsp, 0), reg.fp());
121 5787 : break;
122 : default:
123 0 : UNREACHABLE();
124 : }
125 15140 : }
126 :
127 : template <typename... Regs>
128 73931 : inline void SpillRegisters(LiftoffAssembler* assm, Regs... regs) {
129 221793 : for (LiftoffRegister r : {LiftoffRegister(regs)...}) {
130 147862 : if (assm->cache_state()->is_used(r)) assm->SpillRegister(r);
131 : }
132 73931 : }
133 :
134 : } // namespace liftoff
135 :
136 : int LiftoffAssembler::PrepareStackFrame() {
137 711453 : int offset = pc_offset();
138 711453 : sub_sp_32(0);
139 : return offset;
140 : }
141 :
142 687172 : void LiftoffAssembler::PatchPrepareStackFrame(int offset,
143 : uint32_t stack_slots) {
144 687172 : uint32_t bytes = liftoff::kConstantStackSpace + kStackSlotSize * stack_slots;
145 : DCHECK_LE(bytes, kMaxInt);
146 : // We can't run out of space, just pass anything big enough to not cause the
147 : // assembler to try to grow the buffer.
148 : constexpr int kAvailableSpace = 64;
149 : Assembler patching_assembler(
150 : AssemblerOptions{},
151 2061975 : ExternalAssemblerBuffer(buffer_start_ + offset, kAvailableSpace));
152 687355 : patching_assembler.sub_sp_32(bytes);
153 687443 : }
154 :
155 : void LiftoffAssembler::FinishCode() {}
156 :
157 : void LiftoffAssembler::AbortCompilation() {}
158 :
159 2115369 : void LiftoffAssembler::LoadConstant(LiftoffRegister reg, WasmValue value,
160 : RelocInfo::Mode rmode) {
161 2115369 : switch (value.type()) {
162 : case kWasmI32:
163 1802727 : if (value.to_i32() == 0 && RelocInfo::IsNone(rmode)) {
164 425169 : xorl(reg.gp(), reg.gp());
165 : } else {
166 1377558 : movl(reg.gp(), Immediate(value.to_i32(), rmode));
167 : }
168 : break;
169 : case kWasmI64:
170 44852 : if (RelocInfo::IsNone(rmode)) {
171 44852 : TurboAssembler::Set(reg.gp(), value.to_i64());
172 : } else {
173 0 : movq(reg.gp(), value.to_i64(), rmode);
174 : }
175 : break;
176 : case kWasmF32:
177 132940 : TurboAssembler::Move(reg.fp(), value.to_f32_boxed().get_bits());
178 132941 : break;
179 : case kWasmF64:
180 134850 : TurboAssembler::Move(reg.fp(), value.to_f64_boxed().get_bits());
181 134851 : break;
182 : default:
183 0 : UNREACHABLE();
184 : }
185 2115337 : }
186 :
187 637940 : void LiftoffAssembler::LoadFromInstance(Register dst, uint32_t offset,
188 : int size) {
189 : DCHECK_LE(offset, kMaxInt);
190 638060 : movp(dst, liftoff::GetInstanceOperand());
191 : DCHECK(size == 4 || size == 8);
192 638056 : if (size == 4) {
193 6355 : movl(dst, Operand(dst, offset));
194 : } else {
195 1269870 : movq(dst, Operand(dst, offset));
196 : }
197 638169 : }
198 :
199 130318 : void LiftoffAssembler::LoadTaggedPointerFromInstance(Register dst,
200 : uint32_t offset) {
201 : DCHECK_LE(offset, kMaxInt);
202 130319 : movp(dst, liftoff::GetInstanceOperand());
203 130318 : LoadTaggedPointerField(dst, Operand(dst, offset));
204 130317 : }
205 :
206 711407 : void LiftoffAssembler::SpillInstance(Register instance) {
207 711525 : movp(liftoff::GetInstanceOperand(), instance);
208 711444 : }
209 :
210 9838 : void LiftoffAssembler::FillInstanceInto(Register dst) {
211 9854 : movp(dst, liftoff::GetInstanceOperand());
212 9861 : }
213 :
214 130267 : void LiftoffAssembler::LoadTaggedPointer(Register dst, Register src_addr,
215 : Register offset_reg,
216 : uint32_t offset_imm,
217 : LiftoffRegList pinned) {
218 130267 : if (emit_debug_code() && offset_reg != no_reg) {
219 0 : AssertZeroExtended(offset_reg);
220 : }
221 130267 : Operand src_op = liftoff::GetMemOp(this, src_addr, offset_reg, offset_imm);
222 130269 : LoadTaggedPointerField(dst, src_op);
223 130270 : }
224 :
225 238549 : void LiftoffAssembler::Load(LiftoffRegister dst, Register src_addr,
226 : Register offset_reg, uint32_t offset_imm,
227 : LoadType type, LiftoffRegList pinned,
228 : uint32_t* protected_load_pc, bool is_load_mem) {
229 342197 : if (emit_debug_code() && offset_reg != no_reg) {
230 0 : AssertZeroExtended(offset_reg);
231 : }
232 238549 : Operand src_op = liftoff::GetMemOp(this, src_addr, offset_reg, offset_imm);
233 342204 : if (protected_load_pc) *protected_load_pc = pc_offset();
234 238556 : switch (type.value()) {
235 : case LoadType::kI32Load8U:
236 : case LoadType::kI64Load8U:
237 792 : movzxbl(dst.gp(), src_op);
238 : break;
239 : case LoadType::kI32Load8S:
240 3134 : movsxbl(dst.gp(), src_op);
241 3134 : break;
242 : case LoadType::kI64Load8S:
243 118 : movsxbq(dst.gp(), src_op);
244 118 : break;
245 : case LoadType::kI32Load16U:
246 : case LoadType::kI64Load16U:
247 458 : movzxwl(dst.gp(), src_op);
248 : break;
249 : case LoadType::kI32Load16S:
250 146 : movsxwl(dst.gp(), src_op);
251 146 : break;
252 : case LoadType::kI64Load16S:
253 136 : movsxwq(dst.gp(), src_op);
254 136 : break;
255 : case LoadType::kI32Load:
256 : case LoadType::kI64Load32U:
257 49895 : movl(dst.gp(), src_op);
258 : break;
259 : case LoadType::kI64Load32S:
260 145 : movsxlq(dst.gp(), src_op);
261 145 : break;
262 : case LoadType::kI64Load:
263 180156 : movq(dst.gp(), src_op);
264 : break;
265 : case LoadType::kF32Load:
266 : Movss(dst.fp(), src_op);
267 : break;
268 : case LoadType::kF64Load:
269 : Movsd(dst.fp(), src_op);
270 : break;
271 : default:
272 0 : UNREACHABLE();
273 : }
274 238552 : }
275 :
276 133865 : void LiftoffAssembler::Store(Register dst_addr, Register offset_reg,
277 : uint32_t offset_imm, LiftoffRegister src,
278 : StoreType type, LiftoffRegList /* pinned */,
279 : uint32_t* protected_store_pc, bool is_store_mem) {
280 267102 : if (emit_debug_code() && offset_reg != no_reg) {
281 0 : AssertZeroExtended(offset_reg);
282 : }
283 133865 : Operand dst_op = liftoff::GetMemOp(this, dst_addr, offset_reg, offset_imm);
284 267105 : if (protected_store_pc) *protected_store_pc = pc_offset();
285 133868 : switch (type.value()) {
286 : case StoreType::kI32Store8:
287 : case StoreType::kI64Store8:
288 995 : movb(dst_op, src.gp());
289 995 : break;
290 : case StoreType::kI32Store16:
291 : case StoreType::kI64Store16:
292 480 : movw(dst_op, src.gp());
293 480 : break;
294 : case StoreType::kI32Store:
295 : case StoreType::kI64Store32:
296 75383 : movl(dst_op, src.gp());
297 : break;
298 : case StoreType::kI64Store:
299 56036 : movq(dst_op, src.gp());
300 : break;
301 : case StoreType::kF32Store:
302 : Movss(dst_op, src.fp());
303 : break;
304 : case StoreType::kF64Store:
305 : Movsd(dst_op, src.fp());
306 : break;
307 : default:
308 0 : UNREACHABLE();
309 : }
310 133866 : }
311 :
312 36721 : void LiftoffAssembler::LoadCallerFrameSlot(LiftoffRegister dst,
313 : uint32_t caller_slot_idx,
314 : ValueType type) {
315 36721 : Operand src(rbp, kSystemPointerSize * (caller_slot_idx + 1));
316 36721 : liftoff::Load(this, dst, src, type);
317 36721 : }
318 :
319 18 : void LiftoffAssembler::MoveStackValue(uint32_t dst_index, uint32_t src_index,
320 : ValueType type) {
321 : DCHECK_NE(dst_index, src_index);
322 18 : Operand src = liftoff::GetStackSlot(src_index);
323 18 : Operand dst = liftoff::GetStackSlot(dst_index);
324 18 : if (ValueTypes::ElementSizeLog2Of(type) == 2) {
325 9 : movl(kScratchRegister, src);
326 : movl(dst, kScratchRegister);
327 : } else {
328 : DCHECK_EQ(3, ValueTypes::ElementSizeLog2Of(type));
329 9 : movq(kScratchRegister, src);
330 : movq(dst, kScratchRegister);
331 : }
332 18 : }
333 :
334 783325 : void LiftoffAssembler::Move(Register dst, Register src, ValueType type) {
335 : DCHECK_NE(dst, src);
336 783325 : if (type == kWasmI32) {
337 636535 : movl(dst, src);
338 : } else {
339 : DCHECK_EQ(kWasmI64, type);
340 146790 : movq(dst, src);
341 : }
342 783327 : }
343 :
344 219774 : void LiftoffAssembler::Move(DoubleRegister dst, DoubleRegister src,
345 : ValueType type) {
346 : DCHECK_NE(dst, src);
347 219774 : if (type == kWasmF32) {
348 : Movss(dst, src);
349 : } else {
350 : DCHECK_EQ(kWasmF64, type);
351 : Movsd(dst, src);
352 : }
353 219777 : }
354 :
355 584881 : void LiftoffAssembler::Spill(uint32_t index, LiftoffRegister reg,
356 : ValueType type) {
357 : RecordUsedSpillSlot(index);
358 584881 : Operand dst = liftoff::GetStackSlot(index);
359 584898 : switch (type) {
360 : case kWasmI32:
361 50846 : movl(dst, reg.gp());
362 : break;
363 : case kWasmI64:
364 33305 : movq(dst, reg.gp());
365 : break;
366 : case kWasmF32:
367 : Movss(dst, reg.fp());
368 : break;
369 : case kWasmF64:
370 : Movsd(dst, reg.fp());
371 : break;
372 : default:
373 0 : UNREACHABLE();
374 : }
375 584895 : }
376 :
377 466 : void LiftoffAssembler::Spill(uint32_t index, WasmValue value) {
378 : RecordUsedSpillSlot(index);
379 466 : Operand dst = liftoff::GetStackSlot(index);
380 466 : switch (value.type()) {
381 : case kWasmI32:
382 264 : movl(dst, Immediate(value.to_i32()));
383 264 : break;
384 : case kWasmI64: {
385 202 : if (is_int32(value.to_i64())) {
386 : // Sign extend low word.
387 202 : movq(dst, Immediate(static_cast<int32_t>(value.to_i64())));
388 0 : } else if (is_uint32(value.to_i64())) {
389 : // Zero extend low word.
390 0 : movl(kScratchRegister, Immediate(static_cast<int32_t>(value.to_i64())));
391 : movq(dst, kScratchRegister);
392 : } else {
393 0 : movq(kScratchRegister, value.to_i64());
394 : movq(dst, kScratchRegister);
395 : }
396 : break;
397 : }
398 : default:
399 : // We do not track f32 and f64 constants, hence they are unreachable.
400 0 : UNREACHABLE();
401 : }
402 466 : }
403 :
404 100050 : void LiftoffAssembler::Fill(LiftoffRegister reg, uint32_t index,
405 : ValueType type) {
406 100050 : Operand src = liftoff::GetStackSlot(index);
407 100056 : switch (type) {
408 : case kWasmI32:
409 35384 : movl(reg.gp(), src);
410 : break;
411 : case kWasmI64:
412 30672 : movq(reg.gp(), src);
413 : break;
414 : case kWasmF32:
415 : Movss(reg.fp(), src);
416 : break;
417 : case kWasmF64:
418 : Movsd(reg.fp(), src);
419 : break;
420 : default:
421 0 : UNREACHABLE();
422 : }
423 100053 : }
424 :
425 0 : void LiftoffAssembler::FillI64Half(Register, uint32_t index, RegPairHalf) {
426 0 : UNREACHABLE();
427 : }
428 :
429 314675 : void LiftoffAssembler::emit_i32_add(Register dst, Register lhs, Register rhs) {
430 314675 : if (lhs != dst) {
431 3134 : leal(dst, Operand(lhs, rhs, times_1, 0));
432 : } else {
433 313108 : addl(dst, rhs);
434 : }
435 314675 : }
436 :
437 28972 : void LiftoffAssembler::emit_i32_sub(Register dst, Register lhs, Register rhs) {
438 28972 : if (dst != rhs) {
439 : // Default path.
440 18063 : if (dst != lhs) movl(dst, lhs);
441 18063 : subl(dst, rhs);
442 10909 : } else if (lhs == rhs) {
443 : // Degenerate case.
444 9 : xorl(dst, dst);
445 : } else {
446 : // Emit {dst = lhs + -rhs} if dst == rhs.
447 10900 : negl(dst);
448 10900 : addl(dst, lhs);
449 : }
450 28973 : }
451 :
452 : namespace liftoff {
453 : template <void (Assembler::*op)(Register, Register),
454 : void (Assembler::*mov)(Register, Register)>
455 100103 : void EmitCommutativeBinOp(LiftoffAssembler* assm, Register dst, Register lhs,
456 : Register rhs) {
457 100103 : if (dst == rhs) {
458 325 : (assm->*op)(dst, lhs);
459 : } else {
460 99778 : if (dst != lhs) (assm->*mov)(dst, lhs);
461 99778 : (assm->*op)(dst, rhs);
462 : }
463 100111 : }
464 : } // namespace liftoff
465 :
466 : void LiftoffAssembler::emit_i32_mul(Register dst, Register lhs, Register rhs) {
467 : liftoff::EmitCommutativeBinOp<&Assembler::imull, &Assembler::movl>(this, dst,
468 24295 : lhs, rhs);
469 : }
470 :
471 : namespace liftoff {
472 : enum class DivOrRem : uint8_t { kDiv, kRem };
473 : template <typename type, DivOrRem div_or_rem>
474 73931 : void EmitIntDivOrRem(LiftoffAssembler* assm, Register dst, Register lhs,
475 : Register rhs, Label* trap_div_by_zero,
476 : Label* trap_div_unrepresentable) {
477 : constexpr bool needs_unrepresentable_check =
478 : std::is_signed<type>::value && div_or_rem == DivOrRem::kDiv;
479 : constexpr bool special_case_minus_1 =
480 : std::is_signed<type>::value && div_or_rem == DivOrRem::kRem;
481 : DCHECK_EQ(needs_unrepresentable_check, trap_div_unrepresentable != nullptr);
482 :
483 : #define iop(name, ...) \
484 : do { \
485 : if (sizeof(type) == 4) { \
486 : assm->name##l(__VA_ARGS__); \
487 : } else { \
488 : assm->name##q(__VA_ARGS__); \
489 : } \
490 : } while (false)
491 :
492 : // For division, the lhs is always taken from {edx:eax}. Thus, make sure that
493 : // these registers are unused. If {rhs} is stored in one of them, move it to
494 : // another temporary register.
495 : // Do all this before any branch, such that the code is executed
496 : // unconditionally, as the cache state will also be modified unconditionally.
497 73931 : liftoff::SpillRegisters(assm, rdx, rax);
498 73931 : if (rhs == rax || rhs == rdx) {
499 69548 : iop(mov, kScratchRegister, rhs);
500 : rhs = kScratchRegister;
501 : }
502 :
503 : // Check for division by zero.
504 73931 : iop(test, rhs, rhs);
505 73931 : assm->j(zero, trap_div_by_zero);
506 :
507 18413 : Label done;
508 : if (needs_unrepresentable_check) {
509 : // Check for {kMinInt / -1}. This is unrepresentable.
510 18604 : Label do_div;
511 18604 : iop(cmp, rhs, Immediate(-1));
512 18604 : assm->j(not_equal, &do_div);
513 : // {lhs} is min int if {lhs - 1} overflows.
514 18604 : iop(cmp, lhs, Immediate(1));
515 18604 : assm->j(overflow, trap_div_unrepresentable);
516 18604 : assm->bind(&do_div);
517 : } else if (special_case_minus_1) {
518 : // {lhs % -1} is always 0 (needs to be special cased because {kMinInt / -1}
519 : // cannot be computed).
520 18413 : Label do_rem;
521 18413 : iop(cmp, rhs, Immediate(-1));
522 18413 : assm->j(not_equal, &do_rem);
523 : // clang-format off
524 : // (conflicts with presubmit checks because it is confused about "xor")
525 : iop(xor, dst, dst);
526 : // clang-format on
527 18413 : assm->jmp(&done);
528 18413 : assm->bind(&do_rem);
529 : }
530 :
531 : // Now move {lhs} into {eax}, then zero-extend or sign-extend into {edx}, then
532 : // do the division.
533 73931 : if (lhs != rax) iop(mov, rax, lhs);
534 : if (std::is_same<int32_t, type>::value) { // i32
535 35342 : assm->cdq();
536 : assm->idivl(rhs);
537 : } else if (std::is_same<uint32_t, type>::value) { // u32
538 : assm->xorl(rdx, rdx);
539 : assm->divl(rhs);
540 : } else if (std::is_same<int64_t, type>::value) { // i64
541 1675 : assm->cqo();
542 : assm->idivq(rhs);
543 : } else { // u64
544 : assm->xorq(rdx, rdx);
545 : assm->divq(rhs);
546 : }
547 :
548 : // Move back the result (in {eax} or {edx}) into the {dst} register.
549 : constexpr Register kResultReg = div_or_rem == DivOrRem::kDiv ? rax : rdx;
550 73931 : if (dst != kResultReg) {
551 : iop(mov, dst, kResultReg);
552 : }
553 18413 : if (special_case_minus_1) assm->bind(&done);
554 73931 : }
555 : } // namespace liftoff
556 :
557 : void LiftoffAssembler::emit_i32_divs(Register dst, Register lhs, Register rhs,
558 : Label* trap_div_by_zero,
559 : Label* trap_div_unrepresentable) {
560 : liftoff::EmitIntDivOrRem<int32_t, liftoff::DivOrRem::kDiv>(
561 17721 : this, dst, lhs, rhs, trap_div_by_zero, trap_div_unrepresentable);
562 : }
563 :
564 : void LiftoffAssembler::emit_i32_divu(Register dst, Register lhs, Register rhs,
565 : Label* trap_div_by_zero) {
566 : liftoff::EmitIntDivOrRem<uint32_t, liftoff::DivOrRem::kDiv>(
567 17692 : this, dst, lhs, rhs, trap_div_by_zero, nullptr);
568 : }
569 :
570 : void LiftoffAssembler::emit_i32_rems(Register dst, Register lhs, Register rhs,
571 : Label* trap_div_by_zero) {
572 : liftoff::EmitIntDivOrRem<int32_t, liftoff::DivOrRem::kRem>(
573 17621 : this, dst, lhs, rhs, trap_div_by_zero, nullptr);
574 : }
575 :
576 : void LiftoffAssembler::emit_i32_remu(Register dst, Register lhs, Register rhs,
577 : Label* trap_div_by_zero) {
578 : liftoff::EmitIntDivOrRem<uint32_t, liftoff::DivOrRem::kRem>(
579 17620 : this, dst, lhs, rhs, trap_div_by_zero, nullptr);
580 : }
581 :
582 : void LiftoffAssembler::emit_i32_and(Register dst, Register lhs, Register rhs) {
583 : liftoff::EmitCommutativeBinOp<&Assembler::andl, &Assembler::movl>(this, dst,
584 25738 : lhs, rhs);
585 : }
586 :
587 : void LiftoffAssembler::emit_i32_or(Register dst, Register lhs, Register rhs) {
588 : liftoff::EmitCommutativeBinOp<&Assembler::orl, &Assembler::movl>(this, dst,
589 16994 : lhs, rhs);
590 : }
591 :
592 : void LiftoffAssembler::emit_i32_xor(Register dst, Register lhs, Register rhs) {
593 : liftoff::EmitCommutativeBinOp<&Assembler::xorl, &Assembler::movl>(this, dst,
594 16843 : lhs, rhs);
595 : }
596 :
597 : namespace liftoff {
598 : template <ValueType type>
599 61852 : inline void EmitShiftOperation(LiftoffAssembler* assm, Register dst,
600 : Register src, Register amount,
601 : void (Assembler::*emit_shift)(Register),
602 : LiftoffRegList pinned) {
603 : // If dst is rcx, compute into the scratch register first, then move to rcx.
604 61852 : if (dst == rcx) {
605 51039 : assm->Move(kScratchRegister, src, type);
606 51039 : if (amount != rcx) assm->Move(rcx, amount, type);
607 51039 : (assm->*emit_shift)(kScratchRegister);
608 51039 : assm->Move(rcx, kScratchRegister, type);
609 112896 : return;
610 : }
611 :
612 : // Move amount into rcx. If rcx is in use, move its content into the scratch
613 : // register. If src is rcx, src is now the scratch register.
614 : bool use_scratch = false;
615 10813 : if (amount != rcx) {
616 25247 : use_scratch = src == rcx ||
617 : assm->cache_state()->is_used(LiftoffRegister(rcx)) ||
618 : pinned.has(LiftoffRegister(rcx));
619 10603 : if (use_scratch) assm->movq(kScratchRegister, rcx);
620 10610 : if (src == rcx) src = kScratchRegister;
621 10610 : assm->Move(rcx, amount, type);
622 : }
623 :
624 : // Do the actual shift.
625 10819 : if (dst != src) assm->Move(dst, src, type);
626 10819 : (assm->*emit_shift)(dst);
627 :
628 : // Restore rcx if needed.
629 10817 : if (use_scratch) assm->movq(rcx, kScratchRegister);
630 : }
631 : } // namespace liftoff
632 :
633 : void LiftoffAssembler::emit_i32_shl(Register dst, Register src, Register amount,
634 : LiftoffRegList pinned) {
635 : liftoff::EmitShiftOperation<kWasmI32>(this, dst, src, amount,
636 17615 : &Assembler::shll_cl, pinned);
637 : }
638 :
639 : void LiftoffAssembler::emit_i32_sar(Register dst, Register src, Register amount,
640 : LiftoffRegList pinned) {
641 : liftoff::EmitShiftOperation<kWasmI32>(this, dst, src, amount,
642 17588 : &Assembler::sarl_cl, pinned);
643 : }
644 :
645 : void LiftoffAssembler::emit_i32_shr(Register dst, Register src, Register amount,
646 : LiftoffRegList pinned) {
647 : liftoff::EmitShiftOperation<kWasmI32>(this, dst, src, amount,
648 17579 : &Assembler::shrl_cl, pinned);
649 : }
650 :
651 : void LiftoffAssembler::emit_i32_shr(Register dst, Register src, int amount) {
652 : if (dst != src) movl(dst, src);
653 : DCHECK(is_uint5(amount));
654 : shrl(dst, Immediate(amount));
655 : }
656 :
657 1320 : bool LiftoffAssembler::emit_i32_clz(Register dst, Register src) {
658 1320 : Label nonzero_input;
659 1320 : Label continuation;
660 1320 : testl(src, src);
661 1320 : j(not_zero, &nonzero_input, Label::kNear);
662 : movl(dst, Immediate(32));
663 1320 : jmp(&continuation, Label::kNear);
664 :
665 1320 : bind(&nonzero_input);
666 : // Get most significant bit set (MSBS).
667 1320 : bsrl(dst, src);
668 : // CLZ = 31 - MSBS = MSBS ^ 31.
669 1320 : xorl(dst, Immediate(31));
670 :
671 1320 : bind(&continuation);
672 1320 : return true;
673 : }
674 :
675 411 : bool LiftoffAssembler::emit_i32_ctz(Register dst, Register src) {
676 411 : Label nonzero_input;
677 411 : Label continuation;
678 411 : testl(src, src);
679 411 : j(not_zero, &nonzero_input, Label::kNear);
680 : movl(dst, Immediate(32));
681 411 : jmp(&continuation, Label::kNear);
682 :
683 411 : bind(&nonzero_input);
684 : // Get least significant bit set, which equals number of trailing zeros.
685 411 : bsfl(dst, src);
686 :
687 411 : bind(&continuation);
688 411 : return true;
689 : }
690 :
691 91 : bool LiftoffAssembler::emit_i32_popcnt(Register dst, Register src) {
692 91 : if (!CpuFeatures::IsSupported(POPCNT)) return false;
693 : CpuFeatureScope scope(this, POPCNT);
694 91 : popcntl(dst, src);
695 : return true;
696 : }
697 :
698 838 : void LiftoffAssembler::emit_i64_add(LiftoffRegister dst, LiftoffRegister lhs,
699 : LiftoffRegister rhs) {
700 838 : if (lhs.gp() != dst.gp()) {
701 1320 : leap(dst.gp(), Operand(lhs.gp(), rhs.gp(), times_1, 0));
702 : } else {
703 178 : addp(dst.gp(), rhs.gp());
704 : }
705 838 : }
706 :
707 991 : void LiftoffAssembler::emit_i64_sub(LiftoffRegister dst, LiftoffRegister lhs,
708 : LiftoffRegister rhs) {
709 991 : if (dst.gp() == rhs.gp()) {
710 192 : negq(dst.gp());
711 192 : addq(dst.gp(), lhs.gp());
712 : } else {
713 799 : if (dst.gp() != lhs.gp()) movq(dst.gp(), lhs.gp());
714 799 : subq(dst.gp(), rhs.gp());
715 : }
716 991 : }
717 :
718 : void LiftoffAssembler::emit_i64_mul(LiftoffRegister dst, LiftoffRegister lhs,
719 : LiftoffRegister rhs) {
720 : liftoff::EmitCommutativeBinOp<&Assembler::imulq, &Assembler::movq>(
721 942 : this, dst.gp(), lhs.gp(), rhs.gp());
722 : }
723 :
724 : bool LiftoffAssembler::emit_i64_divs(LiftoffRegister dst, LiftoffRegister lhs,
725 : LiftoffRegister rhs,
726 : Label* trap_div_by_zero,
727 : Label* trap_div_unrepresentable) {
728 : liftoff::EmitIntDivOrRem<int64_t, liftoff::DivOrRem::kDiv>(
729 : this, dst.gp(), lhs.gp(), rhs.gp(), trap_div_by_zero,
730 883 : trap_div_unrepresentable);
731 : return true;
732 : }
733 :
734 : bool LiftoffAssembler::emit_i64_divu(LiftoffRegister dst, LiftoffRegister lhs,
735 : LiftoffRegister rhs,
736 : Label* trap_div_by_zero) {
737 : liftoff::EmitIntDivOrRem<uint64_t, liftoff::DivOrRem::kDiv>(
738 810 : this, dst.gp(), lhs.gp(), rhs.gp(), trap_div_by_zero, nullptr);
739 : return true;
740 : }
741 :
742 : bool LiftoffAssembler::emit_i64_rems(LiftoffRegister dst, LiftoffRegister lhs,
743 : LiftoffRegister rhs,
744 : Label* trap_div_by_zero) {
745 : liftoff::EmitIntDivOrRem<int64_t, liftoff::DivOrRem::kRem>(
746 792 : this, dst.gp(), lhs.gp(), rhs.gp(), trap_div_by_zero, nullptr);
747 : return true;
748 : }
749 :
750 : bool LiftoffAssembler::emit_i64_remu(LiftoffRegister dst, LiftoffRegister lhs,
751 : LiftoffRegister rhs,
752 : Label* trap_div_by_zero) {
753 : liftoff::EmitIntDivOrRem<uint64_t, liftoff::DivOrRem::kRem>(
754 792 : this, dst.gp(), lhs.gp(), rhs.gp(), trap_div_by_zero, nullptr);
755 : return true;
756 : }
757 :
758 : void LiftoffAssembler::emit_i64_and(LiftoffRegister dst, LiftoffRegister lhs,
759 : LiftoffRegister rhs) {
760 : liftoff::EmitCommutativeBinOp<&Assembler::andq, &Assembler::movq>(
761 8637 : this, dst.gp(), lhs.gp(), rhs.gp());
762 : }
763 :
764 : void LiftoffAssembler::emit_i64_or(LiftoffRegister dst, LiftoffRegister lhs,
765 : LiftoffRegister rhs) {
766 : liftoff::EmitCommutativeBinOp<&Assembler::orq, &Assembler::movq>(
767 6595 : this, dst.gp(), lhs.gp(), rhs.gp());
768 : }
769 :
770 : void LiftoffAssembler::emit_i64_xor(LiftoffRegister dst, LiftoffRegister lhs,
771 : LiftoffRegister rhs) {
772 : liftoff::EmitCommutativeBinOp<&Assembler::xorq, &Assembler::movq>(
773 63 : this, dst.gp(), lhs.gp(), rhs.gp());
774 : }
775 :
776 7365 : void LiftoffAssembler::emit_i64_shl(LiftoffRegister dst, LiftoffRegister src,
777 : Register amount, LiftoffRegList pinned) {
778 : liftoff::EmitShiftOperation<kWasmI64>(this, dst.gp(), src.gp(), amount,
779 14730 : &Assembler::shlq_cl, pinned);
780 7371 : }
781 :
782 776 : void LiftoffAssembler::emit_i64_sar(LiftoffRegister dst, LiftoffRegister src,
783 : Register amount, LiftoffRegList pinned) {
784 : liftoff::EmitShiftOperation<kWasmI64>(this, dst.gp(), src.gp(), amount,
785 1552 : &Assembler::sarq_cl, pinned);
786 776 : }
787 :
788 929 : void LiftoffAssembler::emit_i64_shr(LiftoffRegister dst, LiftoffRegister src,
789 : Register amount, LiftoffRegList pinned) {
790 : liftoff::EmitShiftOperation<kWasmI64>(this, dst.gp(), src.gp(), amount,
791 1858 : &Assembler::shrq_cl, pinned);
792 929 : }
793 :
794 546 : void LiftoffAssembler::emit_i64_shr(LiftoffRegister dst, LiftoffRegister src,
795 : int amount) {
796 546 : if (dst.gp() != src.gp()) movl(dst.gp(), src.gp());
797 : DCHECK(is_uint6(amount));
798 546 : shrq(dst.gp(), Immediate(amount));
799 546 : }
800 :
801 : void LiftoffAssembler::emit_i32_to_intptr(Register dst, Register src) {
802 36 : movsxlq(dst, src);
803 : }
804 :
805 510 : void LiftoffAssembler::emit_f32_add(DoubleRegister dst, DoubleRegister lhs,
806 : DoubleRegister rhs) {
807 510 : if (CpuFeatures::IsSupported(AVX)) {
808 : CpuFeatureScope scope(this, AVX);
809 510 : vaddss(dst, lhs, rhs);
810 0 : } else if (dst == rhs) {
811 0 : addss(dst, lhs);
812 : } else {
813 0 : if (dst != lhs) movss(dst, lhs);
814 0 : addss(dst, rhs);
815 : }
816 510 : }
817 :
818 407 : void LiftoffAssembler::emit_f32_sub(DoubleRegister dst, DoubleRegister lhs,
819 : DoubleRegister rhs) {
820 407 : if (CpuFeatures::IsSupported(AVX)) {
821 : CpuFeatureScope scope(this, AVX);
822 407 : vsubss(dst, lhs, rhs);
823 0 : } else if (dst == rhs) {
824 0 : movss(kScratchDoubleReg, rhs);
825 0 : movss(dst, lhs);
826 0 : subss(dst, kScratchDoubleReg);
827 : } else {
828 0 : if (dst != lhs) movss(dst, lhs);
829 0 : subss(dst, rhs);
830 : }
831 407 : }
832 :
833 505 : void LiftoffAssembler::emit_f32_mul(DoubleRegister dst, DoubleRegister lhs,
834 : DoubleRegister rhs) {
835 505 : if (CpuFeatures::IsSupported(AVX)) {
836 : CpuFeatureScope scope(this, AVX);
837 505 : vmulss(dst, lhs, rhs);
838 0 : } else if (dst == rhs) {
839 0 : mulss(dst, lhs);
840 : } else {
841 0 : if (dst != lhs) movss(dst, lhs);
842 0 : mulss(dst, rhs);
843 : }
844 505 : }
845 :
846 429 : void LiftoffAssembler::emit_f32_div(DoubleRegister dst, DoubleRegister lhs,
847 : DoubleRegister rhs) {
848 429 : if (CpuFeatures::IsSupported(AVX)) {
849 : CpuFeatureScope scope(this, AVX);
850 429 : vdivss(dst, lhs, rhs);
851 0 : } else if (dst == rhs) {
852 0 : movss(kScratchDoubleReg, rhs);
853 0 : movss(dst, lhs);
854 0 : divss(dst, kScratchDoubleReg);
855 : } else {
856 0 : if (dst != lhs) movss(dst, lhs);
857 0 : divss(dst, rhs);
858 : }
859 429 : }
860 :
861 : namespace liftoff {
862 : enum class MinOrMax : uint8_t { kMin, kMax };
863 : template <typename type>
864 157 : inline void EmitFloatMinOrMax(LiftoffAssembler* assm, DoubleRegister dst,
865 : DoubleRegister lhs, DoubleRegister rhs,
866 : MinOrMax min_or_max) {
867 157 : Label is_nan;
868 157 : Label lhs_below_rhs;
869 157 : Label lhs_above_rhs;
870 157 : Label done;
871 :
872 : #define dop(name, ...) \
873 : do { \
874 : if (sizeof(type) == 4) { \
875 : assm->name##s(__VA_ARGS__); \
876 : } else { \
877 : assm->name##d(__VA_ARGS__); \
878 : } \
879 : } while (false)
880 :
881 : // Check the easy cases first: nan (e.g. unordered), smaller and greater.
882 : // NaN has to be checked first, because PF=1 implies CF=1.
883 : dop(Ucomis, lhs, rhs);
884 157 : assm->j(parity_even, &is_nan, Label::kNear); // PF=1
885 157 : assm->j(below, &lhs_below_rhs, Label::kNear); // CF=1
886 157 : assm->j(above, &lhs_above_rhs, Label::kNear); // CF=0 && ZF=0
887 :
888 : // If we get here, then either
889 : // a) {lhs == rhs},
890 : // b) {lhs == -0.0} and {rhs == 0.0}, or
891 : // c) {lhs == 0.0} and {rhs == -0.0}.
892 : // For a), it does not matter whether we return {lhs} or {rhs}. Check the sign
893 : // bit of {rhs} to differentiate b) and c).
894 : dop(Movmskp, kScratchRegister, rhs);
895 : assm->testl(kScratchRegister, Immediate(1));
896 156 : assm->j(zero, &lhs_below_rhs, Label::kNear);
897 156 : assm->jmp(&lhs_above_rhs, Label::kNear);
898 :
899 156 : assm->bind(&is_nan);
900 : // Create a NaN output.
901 : dop(Xorp, dst, dst);
902 : dop(Divs, dst, dst);
903 156 : assm->jmp(&done, Label::kNear);
904 :
905 156 : assm->bind(&lhs_below_rhs);
906 156 : DoubleRegister lhs_below_rhs_src = min_or_max == MinOrMax::kMin ? lhs : rhs;
907 156 : if (dst != lhs_below_rhs_src) dop(Movs, dst, lhs_below_rhs_src);
908 156 : assm->jmp(&done, Label::kNear);
909 :
910 157 : assm->bind(&lhs_above_rhs);
911 157 : DoubleRegister lhs_above_rhs_src = min_or_max == MinOrMax::kMin ? rhs : lhs;
912 157 : if (dst != lhs_above_rhs_src) dop(Movs, dst, lhs_above_rhs_src);
913 :
914 157 : assm->bind(&done);
915 157 : }
916 : } // namespace liftoff
917 :
918 : void LiftoffAssembler::emit_f32_min(DoubleRegister dst, DoubleRegister lhs,
919 : DoubleRegister rhs) {
920 : liftoff::EmitFloatMinOrMax<float>(this, dst, lhs, rhs,
921 37 : liftoff::MinOrMax::kMin);
922 : }
923 :
924 : void LiftoffAssembler::emit_f32_max(DoubleRegister dst, DoubleRegister lhs,
925 : DoubleRegister rhs) {
926 : liftoff::EmitFloatMinOrMax<float>(this, dst, lhs, rhs,
927 37 : liftoff::MinOrMax::kMax);
928 : }
929 :
930 32 : void LiftoffAssembler::emit_f32_copysign(DoubleRegister dst, DoubleRegister lhs,
931 : DoubleRegister rhs) {
932 : static constexpr int kF32SignBit = 1 << 31;
933 : Movd(kScratchRegister, lhs);
934 32 : andl(kScratchRegister, Immediate(~kF32SignBit));
935 : Movd(liftoff::kScratchRegister2, rhs);
936 32 : andl(liftoff::kScratchRegister2, Immediate(kF32SignBit));
937 32 : orl(kScratchRegister, liftoff::kScratchRegister2);
938 : Movd(dst, kScratchRegister);
939 32 : }
940 :
941 38 : void LiftoffAssembler::emit_f32_abs(DoubleRegister dst, DoubleRegister src) {
942 : static constexpr uint32_t kSignBit = uint32_t{1} << 31;
943 38 : if (dst == src) {
944 10 : TurboAssembler::Move(kScratchDoubleReg, kSignBit - 1);
945 : Andps(dst, kScratchDoubleReg);
946 : } else {
947 28 : TurboAssembler::Move(dst, kSignBit - 1);
948 : Andps(dst, src);
949 : }
950 38 : }
951 :
952 255 : void LiftoffAssembler::emit_f32_neg(DoubleRegister dst, DoubleRegister src) {
953 : static constexpr uint32_t kSignBit = uint32_t{1} << 31;
954 255 : if (dst == src) {
955 50 : TurboAssembler::Move(kScratchDoubleReg, kSignBit);
956 : Xorps(dst, kScratchDoubleReg);
957 : } else {
958 205 : TurboAssembler::Move(dst, kSignBit);
959 : Xorps(dst, src);
960 : }
961 256 : }
962 :
963 23 : bool LiftoffAssembler::emit_f32_ceil(DoubleRegister dst, DoubleRegister src) {
964 23 : if (CpuFeatures::IsSupported(SSE4_1)) {
965 : CpuFeatureScope feature(this, SSE4_1);
966 : Roundss(dst, src, kRoundUp);
967 : return true;
968 : }
969 : return false;
970 : }
971 :
972 23 : bool LiftoffAssembler::emit_f32_floor(DoubleRegister dst, DoubleRegister src) {
973 23 : if (CpuFeatures::IsSupported(SSE4_1)) {
974 : CpuFeatureScope feature(this, SSE4_1);
975 : Roundss(dst, src, kRoundDown);
976 : return true;
977 : }
978 : return false;
979 : }
980 :
981 23 : bool LiftoffAssembler::emit_f32_trunc(DoubleRegister dst, DoubleRegister src) {
982 23 : if (CpuFeatures::IsSupported(SSE4_1)) {
983 : CpuFeatureScope feature(this, SSE4_1);
984 : Roundss(dst, src, kRoundToZero);
985 : return true;
986 : }
987 : return false;
988 : }
989 :
990 23 : bool LiftoffAssembler::emit_f32_nearest_int(DoubleRegister dst,
991 : DoubleRegister src) {
992 23 : if (CpuFeatures::IsSupported(SSE4_1)) {
993 : CpuFeatureScope feature(this, SSE4_1);
994 : Roundss(dst, src, kRoundToNearest);
995 : return true;
996 : }
997 : return false;
998 : }
999 :
1000 : void LiftoffAssembler::emit_f32_sqrt(DoubleRegister dst, DoubleRegister src) {
1001 : Sqrtss(dst, src);
1002 : }
1003 :
1004 942 : void LiftoffAssembler::emit_f64_add(DoubleRegister dst, DoubleRegister lhs,
1005 : DoubleRegister rhs) {
1006 942 : if (CpuFeatures::IsSupported(AVX)) {
1007 : CpuFeatureScope scope(this, AVX);
1008 942 : vaddsd(dst, lhs, rhs);
1009 0 : } else if (dst == rhs) {
1010 0 : addsd(dst, lhs);
1011 : } else {
1012 0 : if (dst != lhs) movsd(dst, lhs);
1013 0 : addsd(dst, rhs);
1014 : }
1015 942 : }
1016 :
1017 411 : void LiftoffAssembler::emit_f64_sub(DoubleRegister dst, DoubleRegister lhs,
1018 : DoubleRegister rhs) {
1019 411 : if (CpuFeatures::IsSupported(AVX)) {
1020 : CpuFeatureScope scope(this, AVX);
1021 411 : vsubsd(dst, lhs, rhs);
1022 0 : } else if (dst == rhs) {
1023 0 : movsd(kScratchDoubleReg, rhs);
1024 0 : movsd(dst, lhs);
1025 0 : subsd(dst, kScratchDoubleReg);
1026 : } else {
1027 0 : if (dst != lhs) movsd(dst, lhs);
1028 0 : subsd(dst, rhs);
1029 : }
1030 411 : }
1031 :
1032 635 : void LiftoffAssembler::emit_f64_mul(DoubleRegister dst, DoubleRegister lhs,
1033 : DoubleRegister rhs) {
1034 635 : if (CpuFeatures::IsSupported(AVX)) {
1035 : CpuFeatureScope scope(this, AVX);
1036 635 : vmulsd(dst, lhs, rhs);
1037 0 : } else if (dst == rhs) {
1038 0 : mulsd(dst, lhs);
1039 : } else {
1040 0 : if (dst != lhs) movsd(dst, lhs);
1041 0 : mulsd(dst, rhs);
1042 : }
1043 636 : }
1044 :
1045 496 : void LiftoffAssembler::emit_f64_div(DoubleRegister dst, DoubleRegister lhs,
1046 : DoubleRegister rhs) {
1047 496 : if (CpuFeatures::IsSupported(AVX)) {
1048 : CpuFeatureScope scope(this, AVX);
1049 496 : vdivsd(dst, lhs, rhs);
1050 0 : } else if (dst == rhs) {
1051 0 : movsd(kScratchDoubleReg, rhs);
1052 0 : movsd(dst, lhs);
1053 0 : divsd(dst, kScratchDoubleReg);
1054 : } else {
1055 0 : if (dst != lhs) movsd(dst, lhs);
1056 0 : divsd(dst, rhs);
1057 : }
1058 497 : }
1059 :
1060 : void LiftoffAssembler::emit_f64_min(DoubleRegister dst, DoubleRegister lhs,
1061 : DoubleRegister rhs) {
1062 : liftoff::EmitFloatMinOrMax<double>(this, dst, lhs, rhs,
1063 46 : liftoff::MinOrMax::kMin);
1064 : }
1065 :
1066 32 : void LiftoffAssembler::emit_f64_copysign(DoubleRegister dst, DoubleRegister lhs,
1067 : DoubleRegister rhs) {
1068 : // Extract sign bit from {rhs} into {kScratchRegister2}.
1069 : Movq(liftoff::kScratchRegister2, rhs);
1070 32 : shrq(liftoff::kScratchRegister2, Immediate(63));
1071 : shlq(liftoff::kScratchRegister2, Immediate(63));
1072 : // Reset sign bit of {lhs} (in {kScratchRegister}).
1073 : Movq(kScratchRegister, lhs);
1074 32 : btrq(kScratchRegister, Immediate(63));
1075 : // Combine both values into {kScratchRegister} and move into {dst}.
1076 32 : orq(kScratchRegister, liftoff::kScratchRegister2);
1077 : Movq(dst, kScratchRegister);
1078 32 : }
1079 :
1080 : void LiftoffAssembler::emit_f64_max(DoubleRegister dst, DoubleRegister lhs,
1081 : DoubleRegister rhs) {
1082 : liftoff::EmitFloatMinOrMax<double>(this, dst, lhs, rhs,
1083 37 : liftoff::MinOrMax::kMax);
1084 : }
1085 :
1086 38 : void LiftoffAssembler::emit_f64_abs(DoubleRegister dst, DoubleRegister src) {
1087 : static constexpr uint64_t kSignBit = uint64_t{1} << 63;
1088 38 : if (dst == src) {
1089 10 : TurboAssembler::Move(kScratchDoubleReg, kSignBit - 1);
1090 : Andpd(dst, kScratchDoubleReg);
1091 : } else {
1092 28 : TurboAssembler::Move(dst, kSignBit - 1);
1093 : Andpd(dst, src);
1094 : }
1095 38 : }
1096 :
1097 256 : void LiftoffAssembler::emit_f64_neg(DoubleRegister dst, DoubleRegister src) {
1098 : static constexpr uint64_t kSignBit = uint64_t{1} << 63;
1099 256 : if (dst == src) {
1100 59 : TurboAssembler::Move(kScratchDoubleReg, kSignBit);
1101 : Xorpd(dst, kScratchDoubleReg);
1102 : } else {
1103 197 : TurboAssembler::Move(dst, kSignBit);
1104 : Xorpd(dst, src);
1105 : }
1106 256 : }
1107 :
1108 23 : bool LiftoffAssembler::emit_f64_ceil(DoubleRegister dst, DoubleRegister src) {
1109 23 : REQUIRE_CPU_FEATURE(SSE4_1, true);
1110 : Roundsd(dst, src, kRoundUp);
1111 : return true;
1112 : }
1113 :
1114 23 : bool LiftoffAssembler::emit_f64_floor(DoubleRegister dst, DoubleRegister src) {
1115 23 : REQUIRE_CPU_FEATURE(SSE4_1, true);
1116 : Roundsd(dst, src, kRoundDown);
1117 : return true;
1118 : }
1119 :
1120 23 : bool LiftoffAssembler::emit_f64_trunc(DoubleRegister dst, DoubleRegister src) {
1121 23 : REQUIRE_CPU_FEATURE(SSE4_1, true);
1122 : Roundsd(dst, src, kRoundToZero);
1123 : return true;
1124 : }
1125 :
1126 23 : bool LiftoffAssembler::emit_f64_nearest_int(DoubleRegister dst,
1127 : DoubleRegister src) {
1128 23 : REQUIRE_CPU_FEATURE(SSE4_1, true);
1129 : Roundsd(dst, src, kRoundToNearest);
1130 : return true;
1131 : }
1132 :
1133 : void LiftoffAssembler::emit_f64_sqrt(DoubleRegister dst, DoubleRegister src) {
1134 : Sqrtsd(dst, src);
1135 : }
1136 :
1137 : namespace liftoff {
1138 : // Used for float to int conversions. If the value in {converted_back} equals
1139 : // {src} afterwards, the conversion succeeded.
1140 : template <typename dst_type, typename src_type>
1141 91 : inline void ConvertFloatToIntAndBack(LiftoffAssembler* assm, Register dst,
1142 : DoubleRegister src,
1143 : DoubleRegister converted_back) {
1144 : if (std::is_same<double, src_type>::value) { // f64
1145 : if (std::is_same<int32_t, dst_type>::value) { // f64 -> i32
1146 135 : assm->Cvttsd2si(dst, src);
1147 135 : assm->Cvtlsi2sd(converted_back, dst);
1148 : } else if (std::is_same<uint32_t, dst_type>::value) { // f64 -> u32
1149 41 : assm->Cvttsd2siq(dst, src);
1150 41 : assm->movl(dst, dst);
1151 41 : assm->Cvtqsi2sd(converted_back, dst);
1152 : } else if (std::is_same<int64_t, dst_type>::value) { // f64 -> i64
1153 719 : assm->Cvttsd2siq(dst, src);
1154 721 : assm->Cvtqsi2sd(converted_back, dst);
1155 : } else {
1156 : UNREACHABLE();
1157 : }
1158 : } else { // f32
1159 : if (std::is_same<int32_t, dst_type>::value) { // f32 -> i32
1160 135 : assm->Cvttss2si(dst, src);
1161 135 : assm->Cvtlsi2ss(converted_back, dst);
1162 : } else if (std::is_same<uint32_t, dst_type>::value) { // f32 -> u32
1163 50 : assm->Cvttss2siq(dst, src);
1164 50 : assm->movl(dst, dst);
1165 50 : assm->Cvtqsi2ss(converted_back, dst);
1166 : } else if (std::is_same<int64_t, dst_type>::value) { // f32 -> i64
1167 41 : assm->Cvttss2siq(dst, src);
1168 41 : assm->Cvtqsi2ss(converted_back, dst);
1169 : } else {
1170 : UNREACHABLE();
1171 : }
1172 : }
1173 91 : }
1174 :
1175 : template <typename dst_type, typename src_type>
1176 1115 : inline bool EmitTruncateFloatToInt(LiftoffAssembler* assm, Register dst,
1177 : DoubleRegister src, Label* trap) {
1178 1115 : if (!CpuFeatures::IsSupported(SSE4_1)) {
1179 : assm->bailout("no SSE4.1");
1180 : return true;
1181 : }
1182 : CpuFeatureScope feature(assm, SSE4_1);
1183 :
1184 : DoubleRegister rounded = kScratchDoubleReg;
1185 : DoubleRegister converted_back = kScratchDoubleReg2;
1186 :
1187 : if (std::is_same<double, src_type>::value) { // f64
1188 : assm->Roundsd(rounded, src, kRoundToZero);
1189 : } else { // f32
1190 : assm->Roundss(rounded, src, kRoundToZero);
1191 : }
1192 91 : ConvertFloatToIntAndBack<dst_type, src_type>(assm, dst, rounded,
1193 : converted_back);
1194 : if (std::is_same<double, src_type>::value) { // f64
1195 : assm->Ucomisd(converted_back, rounded);
1196 : } else { // f32
1197 : assm->Ucomiss(converted_back, rounded);
1198 : }
1199 :
1200 : // Jump to trap if PF is 0 (one of the operands was NaN) or they are not
1201 : // equal.
1202 1125 : assm->j(parity_even, trap);
1203 1126 : assm->j(not_equal, trap);
1204 : return true;
1205 : }
1206 : } // namespace liftoff
1207 :
1208 130781 : bool LiftoffAssembler::emit_type_conversion(WasmOpcode opcode,
1209 : LiftoffRegister dst,
1210 : LiftoffRegister src, Label* trap) {
1211 130781 : switch (opcode) {
1212 : case kExprI32ConvertI64:
1213 916 : movl(dst.gp(), src.gp());
1214 916 : return true;
1215 : case kExprI32SConvertF32:
1216 : return liftoff::EmitTruncateFloatToInt<int32_t, float>(this, dst.gp(),
1217 135 : src.fp(), trap);
1218 : case kExprI32UConvertF32:
1219 : return liftoff::EmitTruncateFloatToInt<uint32_t, float>(this, dst.gp(),
1220 50 : src.fp(), trap);
1221 : case kExprI32SConvertF64:
1222 : return liftoff::EmitTruncateFloatToInt<int32_t, double>(this, dst.gp(),
1223 135 : src.fp(), trap);
1224 : case kExprI32UConvertF64:
1225 : return liftoff::EmitTruncateFloatToInt<uint32_t, double>(this, dst.gp(),
1226 41 : src.fp(), trap);
1227 : case kExprI32ReinterpretF32:
1228 : Movd(dst.gp(), src.fp());
1229 57285 : return true;
1230 : case kExprI64SConvertI32:
1231 82 : movsxlq(dst.gp(), src.gp());
1232 82 : return true;
1233 : case kExprI64SConvertF32:
1234 : return liftoff::EmitTruncateFloatToInt<int64_t, float>(this, dst.gp(),
1235 41 : src.fp(), trap);
1236 : case kExprI64UConvertF32: {
1237 41 : REQUIRE_CPU_FEATURE(SSE4_1, true);
1238 41 : Cvttss2uiq(dst.gp(), src.fp(), trap);
1239 : return true;
1240 : }
1241 : case kExprI64SConvertF64:
1242 : return liftoff::EmitTruncateFloatToInt<int64_t, double>(this, dst.gp(),
1243 715 : src.fp(), trap);
1244 : case kExprI64UConvertF64: {
1245 50 : REQUIRE_CPU_FEATURE(SSE4_1, true);
1246 50 : Cvttsd2uiq(dst.gp(), src.fp(), trap);
1247 : return true;
1248 : }
1249 : case kExprI64UConvertI32:
1250 13171 : AssertZeroExtended(src.gp());
1251 13171 : if (dst.gp() != src.gp()) movl(dst.gp(), src.gp());
1252 : return true;
1253 : case kExprI64ReinterpretF64:
1254 : Movq(dst.gp(), src.fp());
1255 56935 : return true;
1256 : case kExprF32SConvertI32:
1257 82 : Cvtlsi2ss(dst.fp(), src.gp());
1258 82 : return true;
1259 : case kExprF32UConvertI32:
1260 45 : movl(kScratchRegister, src.gp());
1261 45 : Cvtqsi2ss(dst.fp(), kScratchRegister);
1262 45 : return true;
1263 : case kExprF32SConvertI64:
1264 50 : Cvtqsi2ss(dst.fp(), src.gp());
1265 50 : return true;
1266 : case kExprF32UConvertI64:
1267 32 : Cvtqui2ss(dst.fp(), src.gp());
1268 32 : return true;
1269 : case kExprF32ConvertF64:
1270 99 : Cvtsd2ss(dst.fp(), src.fp());
1271 98 : return true;
1272 : case kExprF32ReinterpretI32:
1273 : Movd(dst.fp(), src.gp());
1274 148 : return true;
1275 : case kExprF64SConvertI32:
1276 68 : Cvtlsi2sd(dst.fp(), src.gp());
1277 68 : return true;
1278 : case kExprF64UConvertI32:
1279 72 : movl(kScratchRegister, src.gp());
1280 72 : Cvtqsi2sd(dst.fp(), kScratchRegister);
1281 72 : return true;
1282 : case kExprF64SConvertI64:
1283 73 : Cvtqsi2sd(dst.fp(), src.gp());
1284 73 : return true;
1285 : case kExprF64UConvertI64:
1286 149 : Cvtqui2sd(dst.fp(), src.gp());
1287 149 : return true;
1288 : case kExprF64ConvertF32:
1289 171 : Cvtss2sd(dst.fp(), src.fp());
1290 170 : return true;
1291 : case kExprF64ReinterpretI64:
1292 : Movq(dst.fp(), src.gp());
1293 194 : return true;
1294 : default:
1295 0 : UNREACHABLE();
1296 : }
1297 : }
1298 :
1299 : void LiftoffAssembler::emit_i32_signextend_i8(Register dst, Register src) {
1300 14 : movsxbl(dst, src);
1301 : }
1302 :
1303 : void LiftoffAssembler::emit_i32_signextend_i16(Register dst, Register src) {
1304 5 : movsxwl(dst, src);
1305 : }
1306 :
1307 : void LiftoffAssembler::emit_i64_signextend_i8(LiftoffRegister dst,
1308 : LiftoffRegister src) {
1309 14 : movsxbq(dst.gp(), src.gp());
1310 : }
1311 :
1312 : void LiftoffAssembler::emit_i64_signextend_i16(LiftoffRegister dst,
1313 : LiftoffRegister src) {
1314 5 : movsxwq(dst.gp(), src.gp());
1315 : }
1316 :
1317 : void LiftoffAssembler::emit_i64_signextend_i32(LiftoffRegister dst,
1318 : LiftoffRegister src) {
1319 5 : movsxlq(dst.gp(), src.gp());
1320 : }
1321 :
1322 363713 : void LiftoffAssembler::emit_jump(Label* label) { jmp(label); }
1323 :
1324 : void LiftoffAssembler::emit_jump(Register target) { jmp(target); }
1325 :
1326 368522 : void LiftoffAssembler::emit_cond_jump(Condition cond, Label* label,
1327 : ValueType type, Register lhs,
1328 : Register rhs) {
1329 368522 : if (rhs != no_reg) {
1330 245029 : switch (type) {
1331 : case kWasmI32:
1332 241799 : cmpl(lhs, rhs);
1333 241796 : break;
1334 : case kWasmI64:
1335 3230 : cmpq(lhs, rhs);
1336 3230 : break;
1337 : default:
1338 0 : UNREACHABLE();
1339 : }
1340 : } else {
1341 : DCHECK_EQ(type, kWasmI32);
1342 123493 : testl(lhs, lhs);
1343 : }
1344 :
1345 368519 : j(cond, label);
1346 368520 : }
1347 :
1348 118027 : void LiftoffAssembler::emit_i32_eqz(Register dst, Register src) {
1349 118027 : testl(src, src);
1350 118028 : setcc(equal, dst);
1351 : movzxbl(dst, dst);
1352 118030 : }
1353 :
1354 248143 : void LiftoffAssembler::emit_i32_set_cond(Condition cond, Register dst,
1355 : Register lhs, Register rhs) {
1356 248143 : cmpl(lhs, rhs);
1357 248142 : setcc(cond, dst);
1358 : movzxbl(dst, dst);
1359 248143 : }
1360 :
1361 186 : void LiftoffAssembler::emit_i64_eqz(Register dst, LiftoffRegister src) {
1362 186 : testq(src.gp(), src.gp());
1363 186 : setcc(equal, dst);
1364 : movzxbl(dst, dst);
1365 186 : }
1366 :
1367 39259 : void LiftoffAssembler::emit_i64_set_cond(Condition cond, Register dst,
1368 : LiftoffRegister lhs,
1369 : LiftoffRegister rhs) {
1370 39259 : cmpq(lhs.gp(), rhs.gp());
1371 39259 : setcc(cond, dst);
1372 : movzxbl(dst, dst);
1373 39259 : }
1374 :
1375 : namespace liftoff {
1376 : template <void (TurboAssembler::*cmp_op)(DoubleRegister, DoubleRegister)>
1377 1311 : void EmitFloatSetCond(LiftoffAssembler* assm, Condition cond, Register dst,
1378 : DoubleRegister lhs, DoubleRegister rhs) {
1379 1311 : Label cont;
1380 1311 : Label not_nan;
1381 :
1382 : (assm->*cmp_op)(lhs, rhs);
1383 : // If PF is one, one of the operands was NaN. This needs special handling.
1384 1313 : assm->j(parity_odd, ¬_nan, Label::kNear);
1385 : // Return 1 for f32.ne, 0 for all other cases.
1386 1318 : if (cond == not_equal) {
1387 : assm->movl(dst, Immediate(1));
1388 : } else {
1389 : assm->xorl(dst, dst);
1390 : }
1391 1315 : assm->jmp(&cont, Label::kNear);
1392 1312 : assm->bind(¬_nan);
1393 :
1394 1315 : assm->setcc(cond, dst);
1395 : assm->movzxbl(dst, dst);
1396 1311 : assm->bind(&cont);
1397 1313 : }
1398 : } // namespace liftoff
1399 :
1400 : void LiftoffAssembler::emit_f32_set_cond(Condition cond, Register dst,
1401 : DoubleRegister lhs,
1402 : DoubleRegister rhs) {
1403 : liftoff::EmitFloatSetCond<&TurboAssembler::Ucomiss>(this, cond, dst, lhs,
1404 708 : rhs);
1405 : }
1406 :
1407 : void LiftoffAssembler::emit_f64_set_cond(Condition cond, Register dst,
1408 : DoubleRegister lhs,
1409 : DoubleRegister rhs) {
1410 : liftoff::EmitFloatSetCond<&TurboAssembler::Ucomisd>(this, cond, dst, lhs,
1411 618 : rhs);
1412 : }
1413 :
1414 262094 : void LiftoffAssembler::StackCheck(Label* ool_code, Register limit_address) {
1415 262094 : cmpp(rsp, Operand(limit_address, 0));
1416 262284 : j(below_equal, ool_code);
1417 261999 : }
1418 :
1419 272030 : void LiftoffAssembler::CallTrapCallbackForTesting() {
1420 272030 : PrepareCallCFunction(0);
1421 272030 : CallCFunction(ExternalReference::wasm_call_trap_callback_for_testing(), 0);
1422 272030 : }
1423 :
1424 : void LiftoffAssembler::AssertUnreachable(AbortReason reason) {
1425 276892 : TurboAssembler::AssertUnreachable(reason);
1426 : }
1427 :
1428 44703 : void LiftoffAssembler::PushRegisters(LiftoffRegList regs) {
1429 : LiftoffRegList gp_regs = regs & kGpCacheRegList;
1430 139976 : while (!gp_regs.is_empty()) {
1431 : LiftoffRegister reg = gp_regs.GetFirstRegSet();
1432 50537 : pushq(reg.gp());
1433 : gp_regs.clear(reg);
1434 : }
1435 : LiftoffRegList fp_regs = regs & kFpCacheRegList;
1436 : unsigned num_fp_regs = fp_regs.GetNumRegsSet();
1437 44736 : if (num_fp_regs) {
1438 13896 : subp(rsp, Immediate(num_fp_regs * kStackSlotSize));
1439 : unsigned offset = 0;
1440 32926 : while (!fp_regs.is_empty()) {
1441 : LiftoffRegister reg = fp_regs.GetFirstRegSet();
1442 38072 : Movsd(Operand(rsp, offset), reg.fp());
1443 : fp_regs.clear(reg);
1444 19048 : offset += sizeof(double);
1445 : }
1446 : DCHECK_EQ(offset, num_fp_regs * sizeof(double));
1447 : }
1448 44751 : }
1449 :
1450 44787 : void LiftoffAssembler::PopRegisters(LiftoffRegList regs) {
1451 : LiftoffRegList fp_regs = regs & kFpCacheRegList;
1452 : unsigned fp_offset = 0;
1453 108630 : while (!fp_regs.is_empty()) {
1454 : LiftoffRegister reg = fp_regs.GetFirstRegSet();
1455 38109 : Movsd(reg.fp(), Operand(rsp, fp_offset));
1456 : fp_regs.clear(reg);
1457 19056 : fp_offset += sizeof(double);
1458 : }
1459 51741 : if (fp_offset) addp(rsp, Immediate(fp_offset));
1460 : LiftoffRegList gp_regs = regs & kGpCacheRegList;
1461 140162 : while (!gp_regs.is_empty()) {
1462 : LiftoffRegister reg = gp_regs.GetLastRegSet();
1463 50595 : popq(reg.gp());
1464 : gp_regs.clear(reg);
1465 : }
1466 44785 : }
1467 :
1468 : void LiftoffAssembler::DropStackSlotsAndRet(uint32_t num_stack_slots) {
1469 : DCHECK_LT(num_stack_slots,
1470 : (1 << 16) / kSystemPointerSize); // 16 bit immediate
1471 966237 : ret(static_cast<int>(num_stack_slots * kSystemPointerSize));
1472 : }
1473 :
1474 101049 : void LiftoffAssembler::CallC(wasm::FunctionSig* sig,
1475 : const LiftoffRegister* args,
1476 : const LiftoffRegister* rets,
1477 : ValueType out_argument_type, int stack_bytes,
1478 : ExternalReference ext_ref) {
1479 33686 : subp(rsp, Immediate(stack_bytes));
1480 :
1481 : int arg_bytes = 0;
1482 101058 : for (ValueType param_type : sig->parameters()) {
1483 67372 : liftoff::Store(this, Operand(rsp, arg_bytes), *args++, param_type);
1484 67372 : arg_bytes += ValueTypes::MemSize(param_type);
1485 : }
1486 : DCHECK_LE(arg_bytes, stack_bytes);
1487 :
1488 : // Pass a pointer to the buffer with the arguments to the C function.
1489 : movp(arg_reg_1, rsp);
1490 :
1491 : constexpr int kNumCCallArgs = 1;
1492 :
1493 : // Now call the C function.
1494 33686 : PrepareCallCFunction(kNumCCallArgs);
1495 33686 : CallCFunction(ext_ref, kNumCCallArgs);
1496 :
1497 : // Move return value to the right register.
1498 : const LiftoffRegister* next_result_reg = rets;
1499 33686 : if (sig->return_count() > 0) {
1500 : DCHECK_EQ(1, sig->return_count());
1501 : constexpr Register kReturnReg = rax;
1502 33686 : if (kReturnReg != next_result_reg->gp()) {
1503 67354 : Move(*next_result_reg, LiftoffRegister(kReturnReg), sig->GetReturn(0));
1504 : }
1505 33686 : ++next_result_reg;
1506 : }
1507 :
1508 : // Load potential output value from the buffer on the stack.
1509 33686 : if (out_argument_type != kWasmStmt) {
1510 0 : liftoff::Load(this, *next_result_reg, Operand(rsp, 0), out_argument_type);
1511 : }
1512 :
1513 33686 : addp(rsp, Immediate(stack_bytes));
1514 33686 : }
1515 :
1516 : void LiftoffAssembler::CallNativeWasmCode(Address addr) {
1517 9864 : near_call(addr, RelocInfo::WASM_CALL);
1518 : }
1519 :
1520 130266 : void LiftoffAssembler::CallIndirect(wasm::FunctionSig* sig,
1521 : compiler::CallDescriptor* call_descriptor,
1522 : Register target) {
1523 130266 : if (target == no_reg) {
1524 0 : popq(kScratchRegister);
1525 : target = kScratchRegister;
1526 : }
1527 130268 : if (FLAG_untrusted_code_mitigations) {
1528 0 : RetpolineCall(target);
1529 : } else {
1530 130268 : call(target);
1531 : }
1532 130268 : }
1533 :
1534 : void LiftoffAssembler::CallRuntimeStub(WasmCode::RuntimeStubId sid) {
1535 : // A direct call to a wasm runtime stub defined in this module.
1536 : // Just encode the stub index. This will be patched at relocation.
1537 398698 : near_call(static_cast<Address>(sid), RelocInfo::WASM_STUB_CALL);
1538 : }
1539 :
1540 50 : void LiftoffAssembler::AllocateStackSlot(Register addr, uint32_t size) {
1541 100 : subp(rsp, Immediate(size));
1542 : movp(addr, rsp);
1543 50 : }
1544 :
1545 : void LiftoffAssembler::DeallocateStackSlot(uint32_t size) {
1546 50 : addp(rsp, Immediate(size));
1547 : }
1548 :
1549 140149 : void LiftoffStackSlots::Construct() {
1550 177358 : for (auto& slot : slots_) {
1551 74418 : const LiftoffAssembler::VarState& src = slot.src_;
1552 37209 : switch (src.loc()) {
1553 : case LiftoffAssembler::VarState::kStack:
1554 21268 : if (src.type() == kWasmI32) {
1555 : // Load i32 values to a register first to ensure they are zero
1556 : // extended.
1557 7287 : asm_->movl(kScratchRegister, liftoff::GetStackSlot(slot.src_index_));
1558 7287 : asm_->pushq(kScratchRegister);
1559 : } else {
1560 : // For all other types, just push the whole (8-byte) stack slot.
1561 : // This is also ok for f32 values (even though we copy 4 uninitialized
1562 : // bytes), because f32 and f64 values are clearly distinguished in
1563 : // Turbofan, so the uninitialized bytes are never accessed.
1564 13981 : asm_->pushq(liftoff::GetStackSlot(slot.src_index_));
1565 : }
1566 : break;
1567 : case LiftoffAssembler::VarState::kRegister:
1568 15140 : liftoff::push(asm_, src.reg(), src.type());
1569 15140 : break;
1570 : case LiftoffAssembler::VarState::KIntConst:
1571 801 : asm_->pushq(Immediate(src.i32_const()));
1572 801 : break;
1573 : }
1574 : }
1575 140149 : }
1576 :
1577 : #undef REQUIRE_CPU_FEATURE
1578 :
1579 : } // namespace wasm
1580 : } // namespace internal
1581 : } // namespace v8
1582 :
1583 : #endif // V8_WASM_BASELINE_X64_LIFTOFF_ASSEMBLER_X64_H_
|