V8 API Reference, 7.2.502.16 (for Deno 0.2.4)
liftoff-assembler-ia32.h
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_IA32_LIFTOFF_ASSEMBLER_IA32_H_
6 #define V8_WASM_BASELINE_IA32_LIFTOFF_ASSEMBLER_IA32_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 // ebp-4 holds the stack marker, ebp-8 is the instance parameter, first stack
27 // slot is located at ebp-16.
28 constexpr int32_t kConstantStackSpace = 8;
29 constexpr int32_t kFirstStackSlotOffset =
30  kConstantStackSpace + LiftoffAssembler::kStackSlotSize;
31 
32 inline Operand GetStackSlot(uint32_t index) {
33  int32_t offset = index * LiftoffAssembler::kStackSlotSize;
34  return Operand(ebp, -kFirstStackSlotOffset - offset);
35 }
36 
37 inline Operand GetHalfStackSlot(uint32_t half_index) {
38  int32_t offset = half_index * (LiftoffAssembler::kStackSlotSize / 2);
39  return Operand(ebp, -kFirstStackSlotOffset - offset);
40 }
41 
42 // TODO(clemensh): Make this a constexpr variable once Operand is constexpr.
43 inline Operand GetInstanceOperand() { return Operand(ebp, -8); }
44 
45 static constexpr LiftoffRegList kByteRegs =
46  LiftoffRegList::FromBits<Register::ListOf<eax, ecx, edx>()>();
47 
48 inline void Load(LiftoffAssembler* assm, LiftoffRegister dst, Register base,
49  int32_t offset, ValueType type) {
50  Operand src(base, offset);
51  switch (type) {
52  case kWasmI32:
53  assm->mov(dst.gp(), src);
54  break;
55  case kWasmI64:
56  assm->mov(dst.low_gp(), src);
57  assm->mov(dst.high_gp(), Operand(base, offset + 4));
58  break;
59  case kWasmF32:
60  assm->movss(dst.fp(), src);
61  break;
62  case kWasmF64:
63  assm->movsd(dst.fp(), src);
64  break;
65  default:
66  UNREACHABLE();
67  }
68 }
69 
70 inline void Store(LiftoffAssembler* assm, Register base, int32_t offset,
71  LiftoffRegister src, ValueType type) {
72  Operand dst(base, offset);
73  switch (type) {
74  case kWasmI32:
75  assm->mov(dst, src.gp());
76  break;
77  case kWasmI64:
78  assm->mov(dst, src.low_gp());
79  assm->mov(Operand(base, offset + 4), src.high_gp());
80  break;
81  case kWasmF32:
82  assm->movss(dst, src.fp());
83  break;
84  case kWasmF64:
85  assm->movsd(dst, src.fp());
86  break;
87  default:
88  UNREACHABLE();
89  }
90 }
91 
92 inline void push(LiftoffAssembler* assm, LiftoffRegister reg, ValueType type) {
93  switch (type) {
94  case kWasmI32:
95  assm->push(reg.gp());
96  break;
97  case kWasmI64:
98  assm->push(reg.high_gp());
99  assm->push(reg.low_gp());
100  break;
101  case kWasmF32:
102  assm->sub(esp, Immediate(sizeof(float)));
103  assm->movss(Operand(esp, 0), reg.fp());
104  break;
105  case kWasmF64:
106  assm->sub(esp, Immediate(sizeof(double)));
107  assm->movsd(Operand(esp, 0), reg.fp());
108  break;
109  default:
110  UNREACHABLE();
111  }
112 }
113 
114 template <typename... Regs>
115 inline void SpillRegisters(LiftoffAssembler* assm, Regs... regs) {
116  for (LiftoffRegister r : {LiftoffRegister(regs)...}) {
117  if (assm->cache_state()->is_used(r)) assm->SpillRegister(r);
118  }
119 }
120 
121 inline void SignExtendI32ToI64(Assembler* assm, LiftoffRegister reg) {
122  assm->mov(reg.high_gp(), reg.low_gp());
123  assm->sar(reg.high_gp(), 31);
124 }
125 
126 constexpr DoubleRegister kScratchDoubleReg = xmm7;
127 
128 constexpr int kSubSpSize = 6; // 6 bytes for "sub esp, <imm32>"
129 
130 } // namespace liftoff
131 
132 int LiftoffAssembler::PrepareStackFrame() {
133  int offset = pc_offset();
134  sub_sp_32(0);
135  DCHECK_EQ(liftoff::kSubSpSize, pc_offset() - offset);
136  return offset;
137 }
138 
139 void LiftoffAssembler::PatchPrepareStackFrame(int offset,
140  uint32_t stack_slots) {
141  uint32_t bytes = liftoff::kConstantStackSpace + kStackSlotSize * stack_slots;
142  DCHECK_LE(bytes, kMaxInt);
143  // We can't run out of space, just pass anything big enough to not cause the
144  // assembler to try to grow the buffer.
145  constexpr int kAvailableSpace = 64;
146  Assembler patching_assembler(AssemblerOptions{}, buffer_ + offset,
147  kAvailableSpace);
148 #if V8_OS_WIN
149  constexpr int kPageSize = 4 * 1024;
150  if (bytes > kPageSize) {
151  // Generate OOL code (at the end of the function, where the current
152  // assembler is pointing) to do the explicit stack limit check (see
153  // https://docs.microsoft.com/en-us/previous-versions/visualstudio/
154  // visual-studio-6.0/aa227153(v=vs.60)).
155  // At the function start, emit a jump to that OOL code (from {offset} to
156  // {pc_offset()}).
157  int ool_offset = pc_offset() - offset;
158  patching_assembler.jmp_rel(ool_offset);
159  DCHECK_GE(liftoff::kSubSpSize, patching_assembler.pc_offset());
160  patching_assembler.Nop(liftoff::kSubSpSize -
161  patching_assembler.pc_offset());
162 
163  // Now generate the OOL code.
164  // Use {edi} as scratch register; it is not being used as parameter
165  // register (see wasm-linkage.h).
166  mov(edi, bytes);
167  AllocateStackFrame(edi);
168  // Jump back to the start of the function (from {pc_offset()} to {offset +
169  // kSubSpSize}).
170  int func_start_offset = offset + liftoff::kSubSpSize - pc_offset();
171  jmp_rel(func_start_offset);
172  return;
173  }
174 #endif
175  patching_assembler.sub_sp_32(bytes);
176  DCHECK_EQ(liftoff::kSubSpSize, patching_assembler.pc_offset());
177 }
178 
179 void LiftoffAssembler::FinishCode() {}
180 
181 void LiftoffAssembler::AbortCompilation() {}
182 
183 void LiftoffAssembler::LoadConstant(LiftoffRegister reg, WasmValue value,
184  RelocInfo::Mode rmode) {
185  switch (value.type()) {
186  case kWasmI32:
187  TurboAssembler::Move(reg.gp(), Immediate(value.to_i32(), rmode));
188  break;
189  case kWasmI64: {
190  DCHECK(RelocInfo::IsNone(rmode));
191  int32_t low_word = value.to_i64();
192  int32_t high_word = value.to_i64() >> 32;
193  TurboAssembler::Move(reg.low_gp(), Immediate(low_word));
194  TurboAssembler::Move(reg.high_gp(), Immediate(high_word));
195  break;
196  }
197  case kWasmF32:
198  TurboAssembler::Move(reg.fp(), value.to_f32_boxed().get_bits());
199  break;
200  case kWasmF64:
201  TurboAssembler::Move(reg.fp(), value.to_f64_boxed().get_bits());
202  break;
203  default:
204  UNREACHABLE();
205  }
206 }
207 
208 void LiftoffAssembler::LoadFromInstance(Register dst, uint32_t offset,
209  int size) {
210  DCHECK_LE(offset, kMaxInt);
211  mov(dst, liftoff::GetInstanceOperand());
212  DCHECK_EQ(4, size);
213  mov(dst, Operand(dst, offset));
214 }
215 
216 void LiftoffAssembler::SpillInstance(Register instance) {
217  mov(liftoff::GetInstanceOperand(), instance);
218 }
219 
220 void LiftoffAssembler::FillInstanceInto(Register dst) {
221  mov(dst, liftoff::GetInstanceOperand());
222 }
223 
224 void LiftoffAssembler::Load(LiftoffRegister dst, Register src_addr,
225  Register offset_reg, uint32_t offset_imm,
226  LoadType type, LiftoffRegList pinned,
227  uint32_t* protected_load_pc, bool is_load_mem) {
228  DCHECK_EQ(type.value_type() == kWasmI64, dst.is_pair());
229  // Wasm memory is limited to a size <2GB, so all offsets can be encoded as
230  // immediate value (in 31 bits, interpreted as signed value).
231  // If the offset is bigger, we always trap and this code is not reached.
232  // Note: We shouldn't have memories larger than 2GiB on 32-bit, but if we
233  // did, we encode {offset_im} as signed, and it will simply wrap around.
234  Operand src_op = offset_reg == no_reg
235  ? Operand(src_addr, bit_cast<int32_t>(offset_imm))
236  : Operand(src_addr, offset_reg, times_1, offset_imm);
237  if (protected_load_pc) *protected_load_pc = pc_offset();
238 
239  switch (type.value()) {
240  case LoadType::kI32Load8U:
241  movzx_b(dst.gp(), src_op);
242  break;
243  case LoadType::kI32Load8S:
244  movsx_b(dst.gp(), src_op);
245  break;
246  case LoadType::kI64Load8U:
247  movzx_b(dst.low_gp(), src_op);
248  xor_(dst.high_gp(), dst.high_gp());
249  break;
250  case LoadType::kI64Load8S:
251  movsx_b(dst.low_gp(), src_op);
252  liftoff::SignExtendI32ToI64(this, dst);
253  break;
254  case LoadType::kI32Load16U:
255  movzx_w(dst.gp(), src_op);
256  break;
257  case LoadType::kI32Load16S:
258  movsx_w(dst.gp(), src_op);
259  break;
260  case LoadType::kI64Load16U:
261  movzx_w(dst.low_gp(), src_op);
262  xor_(dst.high_gp(), dst.high_gp());
263  break;
264  case LoadType::kI64Load16S:
265  movsx_w(dst.low_gp(), src_op);
266  liftoff::SignExtendI32ToI64(this, dst);
267  break;
268  case LoadType::kI32Load:
269  mov(dst.gp(), src_op);
270  break;
271  case LoadType::kI64Load32U:
272  mov(dst.low_gp(), src_op);
273  xor_(dst.high_gp(), dst.high_gp());
274  break;
275  case LoadType::kI64Load32S:
276  mov(dst.low_gp(), src_op);
277  liftoff::SignExtendI32ToI64(this, dst);
278  break;
279  case LoadType::kI64Load: {
280  // Compute the operand for the load of the upper half.
281  Operand upper_src_op =
282  offset_reg == no_reg
283  ? Operand(src_addr, bit_cast<int32_t>(offset_imm + 4))
284  : Operand(src_addr, offset_reg, times_1, offset_imm + 4);
285  // The high word has to be mov'ed first, such that this is the protected
286  // instruction. The mov of the low word cannot segfault.
287  mov(dst.high_gp(), upper_src_op);
288  mov(dst.low_gp(), src_op);
289  break;
290  }
291  case LoadType::kF32Load:
292  movss(dst.fp(), src_op);
293  break;
294  case LoadType::kF64Load:
295  movsd(dst.fp(), src_op);
296  break;
297  default:
298  UNREACHABLE();
299  }
300 }
301 
302 void LiftoffAssembler::Store(Register dst_addr, Register offset_reg,
303  uint32_t offset_imm, LiftoffRegister src,
304  StoreType type, LiftoffRegList pinned,
305  uint32_t* protected_store_pc, bool is_store_mem) {
306  DCHECK_EQ(type.value_type() == kWasmI64, src.is_pair());
307  // Wasm memory is limited to a size <2GB, so all offsets can be encoded as
308  // immediate value (in 31 bits, interpreted as signed value).
309  // If the offset is bigger, we always trap and this code is not reached.
310  Operand dst_op = offset_reg == no_reg
311  ? Operand(dst_addr, bit_cast<int32_t>(offset_imm))
312  : Operand(dst_addr, offset_reg, times_1, offset_imm);
313  if (protected_store_pc) *protected_store_pc = pc_offset();
314 
315  switch (type.value()) {
316  case StoreType::kI64Store8:
317  src = src.low();
318  V8_FALLTHROUGH;
319  case StoreType::kI32Store8:
320  // Only the lower 4 registers can be addressed as 8-bit registers.
321  if (src.gp().is_byte_register()) {
322  mov_b(dst_op, src.gp());
323  } else {
324  // We know that {src} is not a byte register, so the only pinned byte
325  // registers (beside the outer {pinned}) are {dst_addr} and potentially
326  // {offset_reg}.
327  LiftoffRegList pinned_byte = pinned | LiftoffRegList::ForRegs(dst_addr);
328  if (offset_reg != no_reg) pinned_byte.set(offset_reg);
329  Register byte_src =
330  GetUnusedRegister(liftoff::kByteRegs, pinned_byte).gp();
331  mov(byte_src, src.gp());
332  mov_b(dst_op, byte_src);
333  }
334  break;
335  case StoreType::kI64Store16:
336  src = src.low();
337  V8_FALLTHROUGH;
338  case StoreType::kI32Store16:
339  mov_w(dst_op, src.gp());
340  break;
341  case StoreType::kI64Store32:
342  src = src.low();
343  V8_FALLTHROUGH;
344  case StoreType::kI32Store:
345  mov(dst_op, src.gp());
346  break;
347  case StoreType::kI64Store: {
348  // Compute the operand for the store of the upper half.
349  Operand upper_dst_op =
350  offset_reg == no_reg
351  ? Operand(dst_addr, bit_cast<int32_t>(offset_imm + 4))
352  : Operand(dst_addr, offset_reg, times_1, offset_imm + 4);
353  // The high word has to be mov'ed first, such that this is the protected
354  // instruction. The mov of the low word cannot segfault.
355  mov(upper_dst_op, src.high_gp());
356  mov(dst_op, src.low_gp());
357  break;
358  }
359  case StoreType::kF32Store:
360  movss(dst_op, src.fp());
361  break;
362  case StoreType::kF64Store:
363  movsd(dst_op, src.fp());
364  break;
365  default:
366  UNREACHABLE();
367  }
368 }
369 
370 void LiftoffAssembler::LoadCallerFrameSlot(LiftoffRegister dst,
371  uint32_t caller_slot_idx,
372  ValueType type) {
373  liftoff::Load(this, dst, ebp, kPointerSize * (caller_slot_idx + 1), type);
374 }
375 
376 void LiftoffAssembler::MoveStackValue(uint32_t dst_index, uint32_t src_index,
377  ValueType type) {
378  DCHECK_NE(dst_index, src_index);
379  if (cache_state_.has_unused_register(kGpReg)) {
380  LiftoffRegister reg = GetUnusedRegister(kGpReg);
381  Fill(reg, src_index, type);
382  Spill(dst_index, reg, type);
383  } else {
384  push(liftoff::GetStackSlot(src_index));
385  pop(liftoff::GetStackSlot(dst_index));
386  }
387 }
388 
389 void LiftoffAssembler::Move(Register dst, Register src, ValueType type) {
390  DCHECK_NE(dst, src);
391  DCHECK_EQ(kWasmI32, type);
392  mov(dst, src);
393 }
394 
395 void LiftoffAssembler::Move(DoubleRegister dst, DoubleRegister src,
396  ValueType type) {
397  DCHECK_NE(dst, src);
398  if (type == kWasmF32) {
399  movss(dst, src);
400  } else {
401  DCHECK_EQ(kWasmF64, type);
402  movsd(dst, src);
403  }
404 }
405 
406 void LiftoffAssembler::Spill(uint32_t index, LiftoffRegister reg,
407  ValueType type) {
408  RecordUsedSpillSlot(index);
409  Operand dst = liftoff::GetStackSlot(index);
410  switch (type) {
411  case kWasmI32:
412  mov(dst, reg.gp());
413  break;
414  case kWasmI64:
415  mov(dst, reg.low_gp());
416  mov(liftoff::GetHalfStackSlot(2 * index - 1), reg.high_gp());
417  break;
418  case kWasmF32:
419  movss(dst, reg.fp());
420  break;
421  case kWasmF64:
422  movsd(dst, reg.fp());
423  break;
424  default:
425  UNREACHABLE();
426  }
427 }
428 
429 void LiftoffAssembler::Spill(uint32_t index, WasmValue value) {
430  RecordUsedSpillSlot(index);
431  Operand dst = liftoff::GetStackSlot(index);
432  switch (value.type()) {
433  case kWasmI32:
434  mov(dst, Immediate(value.to_i32()));
435  break;
436  case kWasmI64: {
437  int32_t low_word = value.to_i64();
438  int32_t high_word = value.to_i64() >> 32;
439  mov(dst, Immediate(low_word));
440  mov(liftoff::GetHalfStackSlot(2 * index - 1), Immediate(high_word));
441  break;
442  }
443  default:
444  // We do not track f32 and f64 constants, hence they are unreachable.
445  UNREACHABLE();
446  }
447 }
448 
449 void LiftoffAssembler::Fill(LiftoffRegister reg, uint32_t index,
450  ValueType type) {
451  Operand src = liftoff::GetStackSlot(index);
452  switch (type) {
453  case kWasmI32:
454  mov(reg.gp(), src);
455  break;
456  case kWasmI64:
457  mov(reg.low_gp(), src);
458  mov(reg.high_gp(), liftoff::GetHalfStackSlot(2 * index - 1));
459  break;
460  case kWasmF32:
461  movss(reg.fp(), src);
462  break;
463  case kWasmF64:
464  movsd(reg.fp(), src);
465  break;
466  default:
467  UNREACHABLE();
468  }
469 }
470 
471 void LiftoffAssembler::FillI64Half(Register reg, uint32_t half_index) {
472  mov(reg, liftoff::GetHalfStackSlot(half_index));
473 }
474 
475 void LiftoffAssembler::emit_i32_add(Register dst, Register lhs, Register rhs) {
476  if (lhs != dst) {
477  lea(dst, Operand(lhs, rhs, times_1, 0));
478  } else {
479  add(dst, rhs);
480  }
481 }
482 
483 void LiftoffAssembler::emit_i32_sub(Register dst, Register lhs, Register rhs) {
484  if (dst == rhs) {
485  neg(dst);
486  add(dst, lhs);
487  } else {
488  if (dst != lhs) mov(dst, lhs);
489  sub(dst, rhs);
490  }
491 }
492 
493 namespace liftoff {
494 template <void (Assembler::*op)(Register, Register)>
495 void EmitCommutativeBinOp(LiftoffAssembler* assm, Register dst, Register lhs,
496  Register rhs) {
497  if (dst == rhs) {
498  (assm->*op)(dst, lhs);
499  } else {
500  if (dst != lhs) assm->mov(dst, lhs);
501  (assm->*op)(dst, rhs);
502  }
503 }
504 } // namespace liftoff
505 
506 void LiftoffAssembler::emit_i32_mul(Register dst, Register lhs, Register rhs) {
507  liftoff::EmitCommutativeBinOp<&Assembler::imul>(this, dst, lhs, rhs);
508 }
509 
510 namespace liftoff {
511 enum class DivOrRem : uint8_t { kDiv, kRem };
512 template <bool is_signed, DivOrRem div_or_rem>
513 void EmitInt32DivOrRem(LiftoffAssembler* assm, Register dst, Register lhs,
514  Register rhs, Label* trap_div_by_zero,
515  Label* trap_div_unrepresentable) {
516  constexpr bool needs_unrepresentable_check =
517  is_signed && div_or_rem == DivOrRem::kDiv;
518  constexpr bool special_case_minus_1 =
519  is_signed && div_or_rem == DivOrRem::kRem;
520  DCHECK_EQ(needs_unrepresentable_check, trap_div_unrepresentable != nullptr);
521 
522  // For division, the lhs is always taken from {edx:eax}. Thus, make sure that
523  // these registers are unused. If {rhs} is stored in one of them, move it to
524  // another temporary register.
525  // Do all this before any branch, such that the code is executed
526  // unconditionally, as the cache state will also be modified unconditionally.
527  liftoff::SpillRegisters(assm, eax, edx);
528  if (rhs == eax || rhs == edx) {
529  LiftoffRegList unavailable = LiftoffRegList::ForRegs(eax, edx, lhs);
530  Register tmp = assm->GetUnusedRegister(kGpReg, unavailable).gp();
531  assm->mov(tmp, rhs);
532  rhs = tmp;
533  }
534 
535  // Check for division by zero.
536  assm->test(rhs, rhs);
537  assm->j(zero, trap_div_by_zero);
538 
539  Label done;
540  if (needs_unrepresentable_check) {
541  // Check for {kMinInt / -1}. This is unrepresentable.
542  Label do_div;
543  assm->cmp(rhs, -1);
544  assm->j(not_equal, &do_div);
545  assm->cmp(lhs, kMinInt);
546  assm->j(equal, trap_div_unrepresentable);
547  assm->bind(&do_div);
548  } else if (special_case_minus_1) {
549  // {lhs % -1} is always 0 (needs to be special cased because {kMinInt / -1}
550  // cannot be computed).
551  Label do_rem;
552  assm->cmp(rhs, -1);
553  assm->j(not_equal, &do_rem);
554  assm->xor_(dst, dst);
555  assm->jmp(&done);
556  assm->bind(&do_rem);
557  }
558 
559  // Now move {lhs} into {eax}, then zero-extend or sign-extend into {edx}, then
560  // do the division.
561  if (lhs != eax) assm->mov(eax, lhs);
562  if (is_signed) {
563  assm->cdq();
564  assm->idiv(rhs);
565  } else {
566  assm->xor_(edx, edx);
567  assm->div(rhs);
568  }
569 
570  // Move back the result (in {eax} or {edx}) into the {dst} register.
571  constexpr Register kResultReg = div_or_rem == DivOrRem::kDiv ? eax : edx;
572  if (dst != kResultReg) assm->mov(dst, kResultReg);
573  if (special_case_minus_1) assm->bind(&done);
574 }
575 } // namespace liftoff
576 
577 void LiftoffAssembler::emit_i32_divs(Register dst, Register lhs, Register rhs,
578  Label* trap_div_by_zero,
579  Label* trap_div_unrepresentable) {
580  liftoff::EmitInt32DivOrRem<true, liftoff::DivOrRem::kDiv>(
581  this, dst, lhs, rhs, trap_div_by_zero, trap_div_unrepresentable);
582 }
583 
584 void LiftoffAssembler::emit_i32_divu(Register dst, Register lhs, Register rhs,
585  Label* trap_div_by_zero) {
586  liftoff::EmitInt32DivOrRem<false, liftoff::DivOrRem::kDiv>(
587  this, dst, lhs, rhs, trap_div_by_zero, nullptr);
588 }
589 
590 void LiftoffAssembler::emit_i32_rems(Register dst, Register lhs, Register rhs,
591  Label* trap_div_by_zero) {
592  liftoff::EmitInt32DivOrRem<true, liftoff::DivOrRem::kRem>(
593  this, dst, lhs, rhs, trap_div_by_zero, nullptr);
594 }
595 
596 void LiftoffAssembler::emit_i32_remu(Register dst, Register lhs, Register rhs,
597  Label* trap_div_by_zero) {
598  liftoff::EmitInt32DivOrRem<false, liftoff::DivOrRem::kRem>(
599  this, dst, lhs, rhs, trap_div_by_zero, nullptr);
600 }
601 
602 void LiftoffAssembler::emit_i32_and(Register dst, Register lhs, Register rhs) {
603  liftoff::EmitCommutativeBinOp<&Assembler::and_>(this, dst, lhs, rhs);
604 }
605 
606 void LiftoffAssembler::emit_i32_or(Register dst, Register lhs, Register rhs) {
607  liftoff::EmitCommutativeBinOp<&Assembler::or_>(this, dst, lhs, rhs);
608 }
609 
610 void LiftoffAssembler::emit_i32_xor(Register dst, Register lhs, Register rhs) {
611  liftoff::EmitCommutativeBinOp<&Assembler::xor_>(this, dst, lhs, rhs);
612 }
613 
614 namespace liftoff {
615 inline void EmitShiftOperation(LiftoffAssembler* assm, Register dst,
616  Register src, Register amount,
617  void (Assembler::*emit_shift)(Register),
618  LiftoffRegList pinned) {
619  pinned.set(dst);
620  pinned.set(src);
621  pinned.set(amount);
622  // If dst is ecx, compute into a tmp register first, then move to ecx.
623  if (dst == ecx) {
624  Register tmp = assm->GetUnusedRegister(kGpReg, pinned).gp();
625  assm->mov(tmp, src);
626  if (amount != ecx) assm->mov(ecx, amount);
627  (assm->*emit_shift)(tmp);
628  assm->mov(ecx, tmp);
629  return;
630  }
631 
632  // Move amount into ecx. If ecx is in use, move its content to a tmp register
633  // first. If src is ecx, src is now the tmp register.
634  Register tmp_reg = no_reg;
635  if (amount != ecx) {
636  if (assm->cache_state()->is_used(LiftoffRegister(ecx)) ||
637  pinned.has(LiftoffRegister(ecx))) {
638  tmp_reg = assm->GetUnusedRegister(kGpReg, pinned).gp();
639  assm->mov(tmp_reg, ecx);
640  if (src == ecx) src = tmp_reg;
641  }
642  assm->mov(ecx, amount);
643  }
644 
645  // Do the actual shift.
646  if (dst != src) assm->mov(dst, src);
647  (assm->*emit_shift)(dst);
648 
649  // Restore ecx if needed.
650  if (tmp_reg.is_valid()) assm->mov(ecx, tmp_reg);
651 }
652 } // namespace liftoff
653 
654 void LiftoffAssembler::emit_i32_shl(Register dst, Register src, Register amount,
655  LiftoffRegList pinned) {
656  liftoff::EmitShiftOperation(this, dst, src, amount, &Assembler::shl_cl,
657  pinned);
658 }
659 
660 void LiftoffAssembler::emit_i32_sar(Register dst, Register src, Register amount,
661  LiftoffRegList pinned) {
662  liftoff::EmitShiftOperation(this, dst, src, amount, &Assembler::sar_cl,
663  pinned);
664 }
665 
666 void LiftoffAssembler::emit_i32_shr(Register dst, Register src, Register amount,
667  LiftoffRegList pinned) {
668  liftoff::EmitShiftOperation(this, dst, src, amount, &Assembler::shr_cl,
669  pinned);
670 }
671 
672 void LiftoffAssembler::emit_i32_shr(Register dst, Register src, int amount) {
673  if (dst != src) mov(dst, src);
674  DCHECK(is_uint5(amount));
675  shr(dst, amount);
676 }
677 
678 bool LiftoffAssembler::emit_i32_clz(Register dst, Register src) {
679  Label nonzero_input;
680  Label continuation;
681  test(src, src);
682  j(not_zero, &nonzero_input, Label::kNear);
683  mov(dst, Immediate(32));
684  jmp(&continuation, Label::kNear);
685 
686  bind(&nonzero_input);
687  // Get most significant bit set (MSBS).
688  bsr(dst, src);
689  // CLZ = 31 - MSBS = MSBS ^ 31.
690  xor_(dst, 31);
691 
692  bind(&continuation);
693  return true;
694 }
695 
696 bool LiftoffAssembler::emit_i32_ctz(Register dst, Register src) {
697  Label nonzero_input;
698  Label continuation;
699  test(src, src);
700  j(not_zero, &nonzero_input, Label::kNear);
701  mov(dst, Immediate(32));
702  jmp(&continuation, Label::kNear);
703 
704  bind(&nonzero_input);
705  // Get least significant bit set, which equals number of trailing zeros.
706  bsf(dst, src);
707 
708  bind(&continuation);
709  return true;
710 }
711 
712 bool LiftoffAssembler::emit_i32_popcnt(Register dst, Register src) {
713  if (!CpuFeatures::IsSupported(POPCNT)) return false;
714  CpuFeatureScope scope(this, POPCNT);
715  popcnt(dst, src);
716  return true;
717 }
718 
719 namespace liftoff {
720 template <void (Assembler::*op)(Register, Register),
721  void (Assembler::*op_with_carry)(Register, Register)>
722 inline void OpWithCarry(LiftoffAssembler* assm, LiftoffRegister dst,
723  LiftoffRegister lhs, LiftoffRegister rhs) {
724  // First, compute the low half of the result, potentially into a temporary dst
725  // register if {dst.low_gp()} equals {rhs.low_gp()} or any register we need to
726  // keep alive for computing the upper half.
727  LiftoffRegList keep_alive = LiftoffRegList::ForRegs(lhs.high_gp(), rhs);
728  Register dst_low = keep_alive.has(dst.low_gp())
729  ? assm->GetUnusedRegister(kGpReg, keep_alive).gp()
730  : dst.low_gp();
731 
732  if (dst_low != lhs.low_gp()) assm->mov(dst_low, lhs.low_gp());
733  (assm->*op)(dst_low, rhs.low_gp());
734 
735  // Now compute the upper half, while keeping alive the previous result.
736  keep_alive = LiftoffRegList::ForRegs(dst_low, rhs.high_gp());
737  Register dst_high = keep_alive.has(dst.high_gp())
738  ? assm->GetUnusedRegister(kGpReg, keep_alive).gp()
739  : dst.high_gp();
740 
741  if (dst_high != lhs.high_gp()) assm->mov(dst_high, lhs.high_gp());
742  (assm->*op_with_carry)(dst_high, rhs.high_gp());
743 
744  // If necessary, move result into the right registers.
745  LiftoffRegister tmp_result = LiftoffRegister::ForPair(dst_low, dst_high);
746  if (tmp_result != dst) assm->Move(dst, tmp_result, kWasmI64);
747 }
748 } // namespace liftoff
749 
750 void LiftoffAssembler::emit_i64_add(LiftoffRegister dst, LiftoffRegister lhs,
751  LiftoffRegister rhs) {
752  liftoff::OpWithCarry<&Assembler::add, &Assembler::adc>(this, dst, lhs, rhs);
753 }
754 
755 void LiftoffAssembler::emit_i64_sub(LiftoffRegister dst, LiftoffRegister lhs,
756  LiftoffRegister rhs) {
757  liftoff::OpWithCarry<&Assembler::sub, &Assembler::sbb>(this, dst, lhs, rhs);
758 }
759 
760 void LiftoffAssembler::emit_i64_mul(LiftoffRegister dst, LiftoffRegister lhs,
761  LiftoffRegister rhs) {
762  // Idea:
763  // [ lhs_hi | lhs_lo ] * [ rhs_hi | rhs_lo ]
764  // = [ lhs_hi * rhs_lo | ] (32 bit mul, shift 32)
765  // + [ lhs_lo * rhs_hi | ] (32 bit mul, shift 32)
766  // + [ lhs_lo * rhs_lo ] (32x32->64 mul, shift 0)
767 
768  // For simplicity, we move lhs and rhs into fixed registers.
769  Register dst_hi = edx;
770  Register dst_lo = eax;
771  Register lhs_hi = ecx;
772  Register lhs_lo = dst_lo;
773  Register rhs_hi = dst_hi;
774  Register rhs_lo = esi;
775 
776  // Spill all these registers if they are still holding other values.
777  liftoff::SpillRegisters(this, dst_hi, dst_lo, lhs_hi, rhs_lo);
778 
779  // Move lhs and rhs into the respective registers.
780  ParallelRegisterMoveTuple reg_moves[]{
781  {LiftoffRegister::ForPair(lhs_lo, lhs_hi), lhs, kWasmI64},
782  {LiftoffRegister::ForPair(rhs_lo, rhs_hi), rhs, kWasmI64}};
783  ParallelRegisterMove(ArrayVector(reg_moves));
784 
785  // First mul: lhs_hi' = lhs_hi * rhs_lo.
786  imul(lhs_hi, rhs_lo);
787  // Second mul: rhi_hi' = rhs_hi * lhs_lo.
788  imul(rhs_hi, lhs_lo);
789  // Add them: lhs_hi'' = lhs_hi' + rhs_hi' = lhs_hi * rhs_lo + rhs_hi * lhs_lo.
790  add(lhs_hi, rhs_hi);
791  // Third mul: edx:eax (dst_hi:dst_lo) = eax * esi (lhs_lo * rhs_lo).
792  mul(rhs_lo);
793  // Add lhs_hi'' to dst_hi.
794  add(dst_hi, lhs_hi);
795 
796  // Finally, move back the temporary result to the actual dst register pair.
797  LiftoffRegister dst_tmp = LiftoffRegister::ForPair(dst_lo, dst_hi);
798  if (dst != dst_tmp) Move(dst, dst_tmp, kWasmI64);
799 }
800 
801 bool LiftoffAssembler::emit_i64_divs(LiftoffRegister dst, LiftoffRegister lhs,
802  LiftoffRegister rhs,
803  Label* trap_div_by_zero,
804  Label* trap_div_unrepresentable) {
805  return false;
806 }
807 
808 bool LiftoffAssembler::emit_i64_divu(LiftoffRegister dst, LiftoffRegister lhs,
809  LiftoffRegister rhs,
810  Label* trap_div_by_zero) {
811  return false;
812 }
813 
814 bool LiftoffAssembler::emit_i64_rems(LiftoffRegister dst, LiftoffRegister lhs,
815  LiftoffRegister rhs,
816  Label* trap_div_by_zero) {
817  return false;
818 }
819 
820 bool LiftoffAssembler::emit_i64_remu(LiftoffRegister dst, LiftoffRegister lhs,
821  LiftoffRegister rhs,
822  Label* trap_div_by_zero) {
823  return false;
824 }
825 
826 namespace liftoff {
827 inline bool PairContains(LiftoffRegister pair, Register reg) {
828  return pair.low_gp() == reg || pair.high_gp() == reg;
829 }
830 
831 inline LiftoffRegister ReplaceInPair(LiftoffRegister pair, Register old_reg,
832  Register new_reg) {
833  if (pair.low_gp() == old_reg) {
834  return LiftoffRegister::ForPair(new_reg, pair.high_gp());
835  }
836  if (pair.high_gp() == old_reg) {
837  return LiftoffRegister::ForPair(pair.low_gp(), new_reg);
838  }
839  return pair;
840 }
841 
842 inline void Emit64BitShiftOperation(
843  LiftoffAssembler* assm, LiftoffRegister dst, LiftoffRegister src,
844  Register amount, void (TurboAssembler::*emit_shift)(Register, Register),
845  LiftoffRegList pinned) {
846  // Temporary registers cannot overlap with {dst}.
847  pinned.set(dst);
848 
849  std::vector<LiftoffAssembler::ParallelRegisterMoveTuple> reg_moves;
850 
851  // If {dst} contains {ecx}, replace it by an unused register, which is then
852  // moved to {ecx} in the end.
853  Register ecx_replace = no_reg;
854  if (PairContains(dst, ecx)) {
855  ecx_replace = assm->GetUnusedRegister(kGpReg, pinned).gp();
856  dst = ReplaceInPair(dst, ecx, ecx_replace);
857  // If {amount} needs to be moved to {ecx}, but {ecx} is in use (and not part
858  // of {dst}, hence overwritten anyway), move {ecx} to a tmp register and
859  // restore it at the end.
860  } else if (amount != ecx &&
861  (assm->cache_state()->is_used(LiftoffRegister(ecx)) ||
862  pinned.has(LiftoffRegister(ecx)))) {
863  ecx_replace = assm->GetUnusedRegister(kGpReg, pinned).gp();
864  reg_moves.emplace_back(ecx_replace, ecx, kWasmI32);
865  }
866 
867  reg_moves.emplace_back(dst, src, kWasmI64);
868  reg_moves.emplace_back(ecx, amount, kWasmI32);
869  assm->ParallelRegisterMove({reg_moves.data(), reg_moves.size()});
870 
871  // Do the actual shift.
872  (assm->*emit_shift)(dst.high_gp(), dst.low_gp());
873 
874  // Restore {ecx} if needed.
875  if (ecx_replace != no_reg) assm->mov(ecx, ecx_replace);
876 }
877 } // namespace liftoff
878 
879 void LiftoffAssembler::emit_i64_shl(LiftoffRegister dst, LiftoffRegister src,
880  Register amount, LiftoffRegList pinned) {
881  liftoff::Emit64BitShiftOperation(this, dst, src, amount,
882  &TurboAssembler::ShlPair_cl, pinned);
883 }
884 
885 void LiftoffAssembler::emit_i64_sar(LiftoffRegister dst, LiftoffRegister src,
886  Register amount, LiftoffRegList pinned) {
887  liftoff::Emit64BitShiftOperation(this, dst, src, amount,
888  &TurboAssembler::SarPair_cl, pinned);
889 }
890 
891 void LiftoffAssembler::emit_i64_shr(LiftoffRegister dst, LiftoffRegister src,
892  Register amount, LiftoffRegList pinned) {
893  liftoff::Emit64BitShiftOperation(this, dst, src, amount,
894  &TurboAssembler::ShrPair_cl, pinned);
895 }
896 
897 void LiftoffAssembler::emit_i64_shr(LiftoffRegister dst, LiftoffRegister src,
898  int amount) {
899  if (dst != src) Move(dst, src, kWasmI64);
900  DCHECK(is_uint6(amount));
901  ShrPair(dst.high_gp(), dst.low_gp(), amount);
902 }
903 
904 void LiftoffAssembler::emit_i32_to_intptr(Register dst, Register src) {
905  // This is a nop on ia32.
906 }
907 
908 void LiftoffAssembler::emit_f32_add(DoubleRegister dst, DoubleRegister lhs,
909  DoubleRegister rhs) {
910  if (CpuFeatures::IsSupported(AVX)) {
911  CpuFeatureScope scope(this, AVX);
912  vaddss(dst, lhs, rhs);
913  } else if (dst == rhs) {
914  addss(dst, lhs);
915  } else {
916  if (dst != lhs) movss(dst, lhs);
917  addss(dst, rhs);
918  }
919 }
920 
921 void LiftoffAssembler::emit_f32_sub(DoubleRegister dst, DoubleRegister lhs,
922  DoubleRegister rhs) {
923  if (CpuFeatures::IsSupported(AVX)) {
924  CpuFeatureScope scope(this, AVX);
925  vsubss(dst, lhs, rhs);
926  } else if (dst == rhs) {
927  movss(liftoff::kScratchDoubleReg, rhs);
928  movss(dst, lhs);
929  subss(dst, liftoff::kScratchDoubleReg);
930  } else {
931  if (dst != lhs) movss(dst, lhs);
932  subss(dst, rhs);
933  }
934 }
935 
936 void LiftoffAssembler::emit_f32_mul(DoubleRegister dst, DoubleRegister lhs,
937  DoubleRegister rhs) {
938  if (CpuFeatures::IsSupported(AVX)) {
939  CpuFeatureScope scope(this, AVX);
940  vmulss(dst, lhs, rhs);
941  } else if (dst == rhs) {
942  mulss(dst, lhs);
943  } else {
944  if (dst != lhs) movss(dst, lhs);
945  mulss(dst, rhs);
946  }
947 }
948 
949 void LiftoffAssembler::emit_f32_div(DoubleRegister dst, DoubleRegister lhs,
950  DoubleRegister rhs) {
951  if (CpuFeatures::IsSupported(AVX)) {
952  CpuFeatureScope scope(this, AVX);
953  vdivss(dst, lhs, rhs);
954  } else if (dst == rhs) {
955  movss(liftoff::kScratchDoubleReg, rhs);
956  movss(dst, lhs);
957  divss(dst, liftoff::kScratchDoubleReg);
958  } else {
959  if (dst != lhs) movss(dst, lhs);
960  divss(dst, rhs);
961  }
962 }
963 
964 namespace liftoff {
965 enum class MinOrMax : uint8_t { kMin, kMax };
966 template <typename type>
967 inline void EmitFloatMinOrMax(LiftoffAssembler* assm, DoubleRegister dst,
968  DoubleRegister lhs, DoubleRegister rhs,
969  MinOrMax min_or_max) {
970  Label is_nan;
971  Label lhs_below_rhs;
972  Label lhs_above_rhs;
973  Label done;
974 
975  // We need one tmp register to extract the sign bit. Get it right at the
976  // beginning, such that the spilling code is not accidentially jumped over.
977  Register tmp = assm->GetUnusedRegister(kGpReg).gp();
978 
979 #define dop(name, ...) \
980  do { \
981  if (sizeof(type) == 4) { \
982  assm->name##s(__VA_ARGS__); \
983  } else { \
984  assm->name##d(__VA_ARGS__); \
985  } \
986  } while (false)
987 
988  // Check the easy cases first: nan (e.g. unordered), smaller and greater.
989  // NaN has to be checked first, because PF=1 implies CF=1.
990  dop(ucomis, lhs, rhs);
991  assm->j(parity_even, &is_nan, Label::kNear); // PF=1
992  assm->j(below, &lhs_below_rhs, Label::kNear); // CF=1
993  assm->j(above, &lhs_above_rhs, Label::kNear); // CF=0 && ZF=0
994 
995  // If we get here, then either
996  // a) {lhs == rhs},
997  // b) {lhs == -0.0} and {rhs == 0.0}, or
998  // c) {lhs == 0.0} and {rhs == -0.0}.
999  // For a), it does not matter whether we return {lhs} or {rhs}. Check the sign
1000  // bit of {rhs} to differentiate b) and c).
1001  dop(movmskp, tmp, rhs);
1002  assm->test(tmp, Immediate(1));
1003  assm->j(zero, &lhs_below_rhs, Label::kNear);
1004  assm->jmp(&lhs_above_rhs, Label::kNear);
1005 
1006  assm->bind(&is_nan);
1007  // Create a NaN output.
1008  dop(xorp, dst, dst);
1009  dop(divs, dst, dst);
1010  assm->jmp(&done, Label::kNear);
1011 
1012  assm->bind(&lhs_below_rhs);
1013  DoubleRegister lhs_below_rhs_src = min_or_max == MinOrMax::kMin ? lhs : rhs;
1014  if (dst != lhs_below_rhs_src) dop(movs, dst, lhs_below_rhs_src);
1015  assm->jmp(&done, Label::kNear);
1016 
1017  assm->bind(&lhs_above_rhs);
1018  DoubleRegister lhs_above_rhs_src = min_or_max == MinOrMax::kMin ? rhs : lhs;
1019  if (dst != lhs_above_rhs_src) dop(movs, dst, lhs_above_rhs_src);
1020 
1021  assm->bind(&done);
1022 }
1023 } // namespace liftoff
1024 
1025 void LiftoffAssembler::emit_f32_min(DoubleRegister dst, DoubleRegister lhs,
1026  DoubleRegister rhs) {
1027  liftoff::EmitFloatMinOrMax<float>(this, dst, lhs, rhs,
1028  liftoff::MinOrMax::kMin);
1029 }
1030 
1031 void LiftoffAssembler::emit_f32_max(DoubleRegister dst, DoubleRegister lhs,
1032  DoubleRegister rhs) {
1033  liftoff::EmitFloatMinOrMax<float>(this, dst, lhs, rhs,
1034  liftoff::MinOrMax::kMax);
1035 }
1036 
1037 void LiftoffAssembler::emit_f32_copysign(DoubleRegister dst, DoubleRegister lhs,
1038  DoubleRegister rhs) {
1039  static constexpr int kF32SignBit = 1 << 31;
1040  Register scratch = GetUnusedRegister(kGpReg).gp();
1041  Register scratch2 =
1042  GetUnusedRegister(kGpReg, LiftoffRegList::ForRegs(scratch)).gp();
1043  Movd(scratch, lhs); // move {lhs} into {scratch}.
1044  and_(scratch, Immediate(~kF32SignBit)); // clear sign bit in {scratch}.
1045  Movd(scratch2, rhs); // move {rhs} into {scratch2}.
1046  and_(scratch2, Immediate(kF32SignBit)); // isolate sign bit in {scratch2}.
1047  or_(scratch, scratch2); // combine {scratch2} into {scratch}.
1048  Movd(dst, scratch); // move result into {dst}.
1049 }
1050 
1051 void LiftoffAssembler::emit_f32_abs(DoubleRegister dst, DoubleRegister src) {
1052  static constexpr uint32_t kSignBit = uint32_t{1} << 31;
1053  if (dst == src) {
1054  TurboAssembler::Move(liftoff::kScratchDoubleReg, kSignBit - 1);
1055  Andps(dst, liftoff::kScratchDoubleReg);
1056  } else {
1057  TurboAssembler::Move(dst, kSignBit - 1);
1058  Andps(dst, src);
1059  }
1060 }
1061 
1062 void LiftoffAssembler::emit_f32_neg(DoubleRegister dst, DoubleRegister src) {
1063  static constexpr uint32_t kSignBit = uint32_t{1} << 31;
1064  if (dst == src) {
1065  TurboAssembler::Move(liftoff::kScratchDoubleReg, kSignBit);
1066  Xorps(dst, liftoff::kScratchDoubleReg);
1067  } else {
1068  TurboAssembler::Move(dst, kSignBit);
1069  Xorps(dst, src);
1070  }
1071 }
1072 
1073 bool LiftoffAssembler::emit_f32_ceil(DoubleRegister dst, DoubleRegister src) {
1074  if (CpuFeatures::IsSupported(SSE4_1)) {
1075  CpuFeatureScope feature(this, SSE4_1);
1076  roundss(dst, src, kRoundUp);
1077  return true;
1078  }
1079  return false;
1080 }
1081 
1082 bool LiftoffAssembler::emit_f32_floor(DoubleRegister dst, DoubleRegister src) {
1083  if (CpuFeatures::IsSupported(SSE4_1)) {
1084  CpuFeatureScope feature(this, SSE4_1);
1085  roundss(dst, src, kRoundDown);
1086  return true;
1087  }
1088  return false;
1089 }
1090 
1091 bool LiftoffAssembler::emit_f32_trunc(DoubleRegister dst, DoubleRegister src) {
1092  if (CpuFeatures::IsSupported(SSE4_1)) {
1093  CpuFeatureScope feature(this, SSE4_1);
1094  roundss(dst, src, kRoundToZero);
1095  return true;
1096  }
1097  return false;
1098 }
1099 
1100 bool LiftoffAssembler::emit_f32_nearest_int(DoubleRegister dst,
1101  DoubleRegister src) {
1102  if (CpuFeatures::IsSupported(SSE4_1)) {
1103  CpuFeatureScope feature(this, SSE4_1);
1104  roundss(dst, src, kRoundToNearest);
1105  return true;
1106  }
1107  return false;
1108 }
1109 
1110 void LiftoffAssembler::emit_f32_sqrt(DoubleRegister dst, DoubleRegister src) {
1111  Sqrtss(dst, src);
1112 }
1113 
1114 void LiftoffAssembler::emit_f64_add(DoubleRegister dst, DoubleRegister lhs,
1115  DoubleRegister rhs) {
1116  if (CpuFeatures::IsSupported(AVX)) {
1117  CpuFeatureScope scope(this, AVX);
1118  vaddsd(dst, lhs, rhs);
1119  } else if (dst == rhs) {
1120  addsd(dst, lhs);
1121  } else {
1122  if (dst != lhs) movsd(dst, lhs);
1123  addsd(dst, rhs);
1124  }
1125 }
1126 
1127 void LiftoffAssembler::emit_f64_sub(DoubleRegister dst, DoubleRegister lhs,
1128  DoubleRegister rhs) {
1129  if (CpuFeatures::IsSupported(AVX)) {
1130  CpuFeatureScope scope(this, AVX);
1131  vsubsd(dst, lhs, rhs);
1132  } else if (dst == rhs) {
1133  movsd(liftoff::kScratchDoubleReg, rhs);
1134  movsd(dst, lhs);
1135  subsd(dst, liftoff::kScratchDoubleReg);
1136  } else {
1137  if (dst != lhs) movsd(dst, lhs);
1138  subsd(dst, rhs);
1139  }
1140 }
1141 
1142 void LiftoffAssembler::emit_f64_mul(DoubleRegister dst, DoubleRegister lhs,
1143  DoubleRegister rhs) {
1144  if (CpuFeatures::IsSupported(AVX)) {
1145  CpuFeatureScope scope(this, AVX);
1146  vmulsd(dst, lhs, rhs);
1147  } else if (dst == rhs) {
1148  mulsd(dst, lhs);
1149  } else {
1150  if (dst != lhs) movsd(dst, lhs);
1151  mulsd(dst, rhs);
1152  }
1153 }
1154 
1155 void LiftoffAssembler::emit_f64_div(DoubleRegister dst, DoubleRegister lhs,
1156  DoubleRegister rhs) {
1157  if (CpuFeatures::IsSupported(AVX)) {
1158  CpuFeatureScope scope(this, AVX);
1159  vdivsd(dst, lhs, rhs);
1160  } else if (dst == rhs) {
1161  movsd(liftoff::kScratchDoubleReg, rhs);
1162  movsd(dst, lhs);
1163  divsd(dst, liftoff::kScratchDoubleReg);
1164  } else {
1165  if (dst != lhs) movsd(dst, lhs);
1166  divsd(dst, rhs);
1167  }
1168 }
1169 
1170 void LiftoffAssembler::emit_f64_min(DoubleRegister dst, DoubleRegister lhs,
1171  DoubleRegister rhs) {
1172  liftoff::EmitFloatMinOrMax<double>(this, dst, lhs, rhs,
1173  liftoff::MinOrMax::kMin);
1174 }
1175 
1176 void LiftoffAssembler::emit_f64_copysign(DoubleRegister dst, DoubleRegister lhs,
1177  DoubleRegister rhs) {
1178  static constexpr int kF32SignBit = 1 << 31;
1179  // On ia32, we cannot hold the whole f64 value in a gp register, so we just
1180  // operate on the upper half (UH).
1181  Register scratch = GetUnusedRegister(kGpReg).gp();
1182  Register scratch2 =
1183  GetUnusedRegister(kGpReg, LiftoffRegList::ForRegs(scratch)).gp();
1184 
1185  Pextrd(scratch, lhs, 1); // move UH of {lhs} into {scratch}.
1186  and_(scratch, Immediate(~kF32SignBit)); // clear sign bit in {scratch}.
1187  Pextrd(scratch2, rhs, 1); // move UH of {rhs} into {scratch2}.
1188  and_(scratch2, Immediate(kF32SignBit)); // isolate sign bit in {scratch2}.
1189  or_(scratch, scratch2); // combine {scratch2} into {scratch}.
1190  movsd(dst, lhs); // move {lhs} into {dst}.
1191  Pinsrd(dst, scratch, 1); // insert {scratch} into UH of {dst}.
1192 }
1193 
1194 void LiftoffAssembler::emit_f64_max(DoubleRegister dst, DoubleRegister lhs,
1195  DoubleRegister rhs) {
1196  liftoff::EmitFloatMinOrMax<double>(this, dst, lhs, rhs,
1197  liftoff::MinOrMax::kMax);
1198 }
1199 
1200 void LiftoffAssembler::emit_f64_abs(DoubleRegister dst, DoubleRegister src) {
1201  static constexpr uint64_t kSignBit = uint64_t{1} << 63;
1202  if (dst == src) {
1203  TurboAssembler::Move(liftoff::kScratchDoubleReg, kSignBit - 1);
1204  Andpd(dst, liftoff::kScratchDoubleReg);
1205  } else {
1206  TurboAssembler::Move(dst, kSignBit - 1);
1207  Andpd(dst, src);
1208  }
1209 }
1210 
1211 void LiftoffAssembler::emit_f64_neg(DoubleRegister dst, DoubleRegister src) {
1212  static constexpr uint64_t kSignBit = uint64_t{1} << 63;
1213  if (dst == src) {
1214  TurboAssembler::Move(liftoff::kScratchDoubleReg, kSignBit);
1215  Xorpd(dst, liftoff::kScratchDoubleReg);
1216  } else {
1217  TurboAssembler::Move(dst, kSignBit);
1218  Xorpd(dst, src);
1219  }
1220 }
1221 
1222 bool LiftoffAssembler::emit_f64_ceil(DoubleRegister dst, DoubleRegister src) {
1223  REQUIRE_CPU_FEATURE(SSE4_1, true);
1224  roundsd(dst, src, kRoundUp);
1225  return true;
1226 }
1227 
1228 bool LiftoffAssembler::emit_f64_floor(DoubleRegister dst, DoubleRegister src) {
1229  REQUIRE_CPU_FEATURE(SSE4_1, true);
1230  roundsd(dst, src, kRoundDown);
1231  return true;
1232 }
1233 
1234 bool LiftoffAssembler::emit_f64_trunc(DoubleRegister dst, DoubleRegister src) {
1235  REQUIRE_CPU_FEATURE(SSE4_1, true);
1236  roundsd(dst, src, kRoundToZero);
1237  return true;
1238 }
1239 
1240 bool LiftoffAssembler::emit_f64_nearest_int(DoubleRegister dst,
1241  DoubleRegister src) {
1242  REQUIRE_CPU_FEATURE(SSE4_1, true);
1243  roundsd(dst, src, kRoundToNearest);
1244  return true;
1245 }
1246 
1247 void LiftoffAssembler::emit_f64_sqrt(DoubleRegister dst, DoubleRegister src) {
1248  Sqrtsd(dst, src);
1249 }
1250 
1251 namespace liftoff {
1252 // Used for float to int conversions. If the value in {converted_back} equals
1253 // {src} afterwards, the conversion succeeded.
1254 template <typename dst_type, typename src_type>
1255 inline void ConvertFloatToIntAndBack(LiftoffAssembler* assm, Register dst,
1256  DoubleRegister src,
1257  DoubleRegister converted_back,
1258  LiftoffRegList pinned) {
1259  if (std::is_same<double, src_type>::value) { // f64
1260  if (std::is_signed<dst_type>::value) { // f64 -> i32
1261  assm->cvttsd2si(dst, src);
1262  assm->Cvtsi2sd(converted_back, dst);
1263  } else { // f64 -> u32
1264  assm->Cvttsd2ui(dst, src, liftoff::kScratchDoubleReg);
1265  assm->Cvtui2sd(converted_back, dst,
1266  assm->GetUnusedRegister(kGpReg, pinned).gp());
1267  }
1268  } else { // f32
1269  if (std::is_signed<dst_type>::value) { // f32 -> i32
1270  assm->cvttss2si(dst, src);
1271  assm->Cvtsi2ss(converted_back, dst);
1272  } else { // f32 -> u32
1273  assm->Cvttss2ui(dst, src, liftoff::kScratchDoubleReg);
1274  assm->Cvtui2ss(converted_back, dst,
1275  assm->GetUnusedRegister(kGpReg, pinned).gp());
1276  }
1277  }
1278 }
1279 
1280 template <typename dst_type, typename src_type>
1281 inline bool EmitTruncateFloatToInt(LiftoffAssembler* assm, Register dst,
1282  DoubleRegister src, Label* trap) {
1283  if (!CpuFeatures::IsSupported(SSE4_1)) {
1284  assm->bailout("no SSE4.1");
1285  return true;
1286  }
1287  CpuFeatureScope feature(assm, SSE4_1);
1288 
1289  LiftoffRegList pinned = LiftoffRegList::ForRegs(src, dst);
1290  DoubleRegister rounded =
1291  pinned.set(assm->GetUnusedRegister(kFpReg, pinned)).fp();
1292  DoubleRegister converted_back =
1293  pinned.set(assm->GetUnusedRegister(kFpReg, pinned)).fp();
1294 
1295  if (std::is_same<double, src_type>::value) { // f64
1296  assm->roundsd(rounded, src, kRoundToZero);
1297  } else { // f32
1298  assm->roundss(rounded, src, kRoundToZero);
1299  }
1300  ConvertFloatToIntAndBack<dst_type, src_type>(assm, dst, rounded,
1301  converted_back, pinned);
1302  if (std::is_same<double, src_type>::value) { // f64
1303  assm->ucomisd(converted_back, rounded);
1304  } else { // f32
1305  assm->ucomiss(converted_back, rounded);
1306  }
1307 
1308  // Jump to trap if PF is 0 (one of the operands was NaN) or they are not
1309  // equal.
1310  assm->j(parity_even, trap);
1311  assm->j(not_equal, trap);
1312  return true;
1313 }
1314 } // namespace liftoff
1315 
1316 bool LiftoffAssembler::emit_type_conversion(WasmOpcode opcode,
1317  LiftoffRegister dst,
1318  LiftoffRegister src, Label* trap) {
1319  switch (opcode) {
1320  case kExprI32ConvertI64:
1321  if (dst.gp() != src.low_gp()) mov(dst.gp(), src.low_gp());
1322  return true;
1323  case kExprI32SConvertF32:
1324  return liftoff::EmitTruncateFloatToInt<int32_t, float>(this, dst.gp(),
1325  src.fp(), trap);
1326  case kExprI32UConvertF32:
1327  return liftoff::EmitTruncateFloatToInt<uint32_t, float>(this, dst.gp(),
1328  src.fp(), trap);
1329  case kExprI32SConvertF64:
1330  return liftoff::EmitTruncateFloatToInt<int32_t, double>(this, dst.gp(),
1331  src.fp(), trap);
1332  case kExprI32UConvertF64:
1333  return liftoff::EmitTruncateFloatToInt<uint32_t, double>(this, dst.gp(),
1334  src.fp(), trap);
1335  case kExprI32ReinterpretF32:
1336  Movd(dst.gp(), src.fp());
1337  return true;
1338  case kExprI64SConvertI32:
1339  if (dst.low_gp() != src.gp()) mov(dst.low_gp(), src.gp());
1340  if (dst.high_gp() != src.gp()) mov(dst.high_gp(), src.gp());
1341  sar(dst.high_gp(), 31);
1342  return true;
1343  case kExprI64UConvertI32:
1344  if (dst.low_gp() != src.gp()) mov(dst.low_gp(), src.gp());
1345  xor_(dst.high_gp(), dst.high_gp());
1346  return true;
1347  case kExprI64ReinterpretF64:
1348  // Push src to the stack.
1349  sub(esp, Immediate(8));
1350  movsd(Operand(esp, 0), src.fp());
1351  // Pop to dst.
1352  pop(dst.low_gp());
1353  pop(dst.high_gp());
1354  return true;
1355  case kExprF32SConvertI32:
1356  cvtsi2ss(dst.fp(), src.gp());
1357  return true;
1358  case kExprF32UConvertI32: {
1359  LiftoffRegList pinned = LiftoffRegList::ForRegs(dst, src);
1360  Register scratch = GetUnusedRegister(kGpReg, pinned).gp();
1361  Cvtui2ss(dst.fp(), src.gp(), scratch);
1362  return true;
1363  }
1364  case kExprF32ConvertF64:
1365  cvtsd2ss(dst.fp(), src.fp());
1366  return true;
1367  case kExprF32ReinterpretI32:
1368  Movd(dst.fp(), src.gp());
1369  return true;
1370  case kExprF64SConvertI32:
1371  Cvtsi2sd(dst.fp(), src.gp());
1372  return true;
1373  case kExprF64UConvertI32: {
1374  LiftoffRegList pinned = LiftoffRegList::ForRegs(dst, src);
1375  Register scratch = GetUnusedRegister(kGpReg, pinned).gp();
1376  Cvtui2sd(dst.fp(), src.gp(), scratch);
1377  return true;
1378  }
1379  case kExprF64ConvertF32:
1380  cvtss2sd(dst.fp(), src.fp());
1381  return true;
1382  case kExprF64ReinterpretI64:
1383  // Push src to the stack.
1384  push(src.high_gp());
1385  push(src.low_gp());
1386  // Pop to dst.
1387  movsd(dst.fp(), Operand(esp, 0));
1388  add(esp, Immediate(8));
1389  return true;
1390  default:
1391  return false;
1392  }
1393 }
1394 
1395 void LiftoffAssembler::emit_i32_signextend_i8(Register dst, Register src) {
1396  movsx_b(dst, src);
1397 }
1398 
1399 void LiftoffAssembler::emit_i32_signextend_i16(Register dst, Register src) {
1400  movsx_w(dst, src);
1401 }
1402 
1403 void LiftoffAssembler::emit_i64_signextend_i8(LiftoffRegister dst,
1404  LiftoffRegister src) {
1405  movsx_b(dst.low_gp(), src.low_gp());
1406  liftoff::SignExtendI32ToI64(this, dst);
1407 }
1408 
1409 void LiftoffAssembler::emit_i64_signextend_i16(LiftoffRegister dst,
1410  LiftoffRegister src) {
1411  movsx_w(dst.low_gp(), src.low_gp());
1412  liftoff::SignExtendI32ToI64(this, dst);
1413 }
1414 
1415 void LiftoffAssembler::emit_i64_signextend_i32(LiftoffRegister dst,
1416  LiftoffRegister src) {
1417  if (dst.low_gp() != src.low_gp()) mov(dst.low_gp(), src.low_gp());
1418  liftoff::SignExtendI32ToI64(this, dst);
1419 }
1420 
1421 void LiftoffAssembler::emit_jump(Label* label) { jmp(label); }
1422 
1423 void LiftoffAssembler::emit_jump(Register target) { jmp(target); }
1424 
1425 void LiftoffAssembler::emit_cond_jump(Condition cond, Label* label,
1426  ValueType type, Register lhs,
1427  Register rhs) {
1428  if (rhs != no_reg) {
1429  switch (type) {
1430  case kWasmI32:
1431  cmp(lhs, rhs);
1432  break;
1433  default:
1434  UNREACHABLE();
1435  }
1436  } else {
1437  DCHECK_EQ(type, kWasmI32);
1438  test(lhs, lhs);
1439  }
1440 
1441  j(cond, label);
1442 }
1443 
1444 namespace liftoff {
1445 
1446 // Get a temporary byte register, using {candidate} if possible.
1447 // Might spill, but always keeps status flags intact.
1448 inline Register GetTmpByteRegister(LiftoffAssembler* assm, Register candidate) {
1449  if (candidate.is_byte_register()) return candidate;
1450  LiftoffRegList pinned = LiftoffRegList::ForRegs(candidate);
1451  // {GetUnusedRegister()} may insert move instructions to spill registers to
1452  // the stack. This is OK because {mov} does not change the status flags.
1453  return assm->GetUnusedRegister(liftoff::kByteRegs, pinned).gp();
1454 }
1455 
1456 // Setcc into dst register, given a scratch byte register (might be the same as
1457 // dst). Never spills.
1458 inline void setcc_32_no_spill(LiftoffAssembler* assm, Condition cond,
1459  Register dst, Register tmp_byte_reg) {
1460  assm->setcc(cond, tmp_byte_reg);
1461  assm->movzx_b(dst, tmp_byte_reg);
1462 }
1463 
1464 // Setcc into dst register (no contraints). Might spill.
1465 inline void setcc_32(LiftoffAssembler* assm, Condition cond, Register dst) {
1466  Register tmp_byte_reg = GetTmpByteRegister(assm, dst);
1467  setcc_32_no_spill(assm, cond, dst, tmp_byte_reg);
1468 }
1469 
1470 } // namespace liftoff
1471 
1472 void LiftoffAssembler::emit_i32_eqz(Register dst, Register src) {
1473  test(src, src);
1474  liftoff::setcc_32(this, equal, dst);
1475 }
1476 
1477 void LiftoffAssembler::emit_i32_set_cond(Condition cond, Register dst,
1478  Register lhs, Register rhs) {
1479  cmp(lhs, rhs);
1480  liftoff::setcc_32(this, cond, dst);
1481 }
1482 
1483 void LiftoffAssembler::emit_i64_eqz(Register dst, LiftoffRegister src) {
1484  // Compute the OR of both registers in the src pair, using dst as scratch
1485  // register. Then check whether the result is equal to zero.
1486  if (src.low_gp() == dst) {
1487  or_(dst, src.high_gp());
1488  } else {
1489  if (src.high_gp() != dst) mov(dst, src.high_gp());
1490  or_(dst, src.low_gp());
1491  }
1492  liftoff::setcc_32(this, equal, dst);
1493 }
1494 
1495 namespace liftoff {
1496 inline Condition cond_make_unsigned(Condition cond) {
1497  switch (cond) {
1498  case kSignedLessThan:
1499  return kUnsignedLessThan;
1500  case kSignedLessEqual:
1501  return kUnsignedLessEqual;
1502  case kSignedGreaterThan:
1503  return kUnsignedGreaterThan;
1504  case kSignedGreaterEqual:
1505  return kUnsignedGreaterEqual;
1506  default:
1507  return cond;
1508  }
1509 }
1510 } // namespace liftoff
1511 
1512 void LiftoffAssembler::emit_i64_set_cond(Condition cond, Register dst,
1513  LiftoffRegister lhs,
1514  LiftoffRegister rhs) {
1515  // Get the tmp byte register out here, such that we don't conditionally spill
1516  // (this cannot be reflected in the cache state).
1517  Register tmp_byte_reg = liftoff::GetTmpByteRegister(this, dst);
1518 
1519  // For signed i64 comparisons, we still need to use unsigned comparison for
1520  // the low word (the only bit carrying signedness information is the MSB in
1521  // the high word).
1522  Condition unsigned_cond = liftoff::cond_make_unsigned(cond);
1523  Label setcc;
1524  Label cont;
1525  // Compare high word first. If it differs, use if for the setcc. If it's
1526  // equal, compare the low word and use that for setcc.
1527  cmp(lhs.high_gp(), rhs.high_gp());
1528  j(not_equal, &setcc, Label::kNear);
1529  cmp(lhs.low_gp(), rhs.low_gp());
1530  if (unsigned_cond != cond) {
1531  // If the condition predicate for the low differs from that for the high
1532  // word, emit a separete setcc sequence for the low word.
1533  liftoff::setcc_32_no_spill(this, unsigned_cond, dst, tmp_byte_reg);
1534  jmp(&cont);
1535  }
1536  bind(&setcc);
1537  liftoff::setcc_32_no_spill(this, cond, dst, tmp_byte_reg);
1538  bind(&cont);
1539 }
1540 
1541 namespace liftoff {
1542 template <void (Assembler::*cmp_op)(DoubleRegister, DoubleRegister)>
1543 void EmitFloatSetCond(LiftoffAssembler* assm, Condition cond, Register dst,
1544  DoubleRegister lhs, DoubleRegister rhs) {
1545  Label cont;
1546  Label not_nan;
1547 
1548  // Get the tmp byte register out here, such that we don't conditionally spill
1549  // (this cannot be reflected in the cache state).
1550  Register tmp_byte_reg = GetTmpByteRegister(assm, dst);
1551 
1552  (assm->*cmp_op)(lhs, rhs);
1553  // If PF is one, one of the operands was Nan. This needs special handling.
1554  assm->j(parity_odd, &not_nan, Label::kNear);
1555  // Return 1 for f32.ne, 0 for all other cases.
1556  if (cond == not_equal) {
1557  assm->mov(dst, Immediate(1));
1558  } else {
1559  assm->xor_(dst, dst);
1560  }
1561  assm->jmp(&cont, Label::kNear);
1562  assm->bind(&not_nan);
1563 
1564  setcc_32_no_spill(assm, cond, dst, tmp_byte_reg);
1565  assm->bind(&cont);
1566 }
1567 } // namespace liftoff
1568 
1569 void LiftoffAssembler::emit_f32_set_cond(Condition cond, Register dst,
1570  DoubleRegister lhs,
1571  DoubleRegister rhs) {
1572  liftoff::EmitFloatSetCond<&Assembler::ucomiss>(this, cond, dst, lhs, rhs);
1573 }
1574 
1575 void LiftoffAssembler::emit_f64_set_cond(Condition cond, Register dst,
1576  DoubleRegister lhs,
1577  DoubleRegister rhs) {
1578  liftoff::EmitFloatSetCond<&Assembler::ucomisd>(this, cond, dst, lhs, rhs);
1579 }
1580 
1581 void LiftoffAssembler::StackCheck(Label* ool_code, Register limit_address) {
1582  cmp(esp, Operand(limit_address, 0));
1583  j(below_equal, ool_code);
1584 }
1585 
1586 void LiftoffAssembler::CallTrapCallbackForTesting() {
1587  PrepareCallCFunction(0, GetUnusedRegister(kGpReg).gp());
1588  CallCFunction(ExternalReference::wasm_call_trap_callback_for_testing(), 0);
1589 }
1590 
1591 void LiftoffAssembler::AssertUnreachable(AbortReason reason) {
1592  TurboAssembler::AssertUnreachable(reason);
1593 }
1594 
1595 void LiftoffAssembler::PushRegisters(LiftoffRegList regs) {
1596  LiftoffRegList gp_regs = regs & kGpCacheRegList;
1597  while (!gp_regs.is_empty()) {
1598  LiftoffRegister reg = gp_regs.GetFirstRegSet();
1599  push(reg.gp());
1600  gp_regs.clear(reg);
1601  }
1602  LiftoffRegList fp_regs = regs & kFpCacheRegList;
1603  unsigned num_fp_regs = fp_regs.GetNumRegsSet();
1604  if (num_fp_regs) {
1605  sub(esp, Immediate(num_fp_regs * kStackSlotSize));
1606  unsigned offset = 0;
1607  while (!fp_regs.is_empty()) {
1608  LiftoffRegister reg = fp_regs.GetFirstRegSet();
1609  movsd(Operand(esp, offset), reg.fp());
1610  fp_regs.clear(reg);
1611  offset += sizeof(double);
1612  }
1613  DCHECK_EQ(offset, num_fp_regs * sizeof(double));
1614  }
1615 }
1616 
1617 void LiftoffAssembler::PopRegisters(LiftoffRegList regs) {
1618  LiftoffRegList fp_regs = regs & kFpCacheRegList;
1619  unsigned fp_offset = 0;
1620  while (!fp_regs.is_empty()) {
1621  LiftoffRegister reg = fp_regs.GetFirstRegSet();
1622  movsd(reg.fp(), Operand(esp, fp_offset));
1623  fp_regs.clear(reg);
1624  fp_offset += sizeof(double);
1625  }
1626  if (fp_offset) add(esp, Immediate(fp_offset));
1627  LiftoffRegList gp_regs = regs & kGpCacheRegList;
1628  while (!gp_regs.is_empty()) {
1629  LiftoffRegister reg = gp_regs.GetLastRegSet();
1630  pop(reg.gp());
1631  gp_regs.clear(reg);
1632  }
1633 }
1634 
1635 void LiftoffAssembler::DropStackSlotsAndRet(uint32_t num_stack_slots) {
1636  DCHECK_LT(num_stack_slots, (1 << 16) / kPointerSize); // 16 bit immediate
1637  ret(static_cast<int>(num_stack_slots * kPointerSize));
1638 }
1639 
1640 void LiftoffAssembler::CallC(wasm::FunctionSig* sig,
1641  const LiftoffRegister* args,
1642  const LiftoffRegister* rets,
1643  ValueType out_argument_type, int stack_bytes,
1644  ExternalReference ext_ref) {
1645  sub(esp, Immediate(stack_bytes));
1646 
1647  int arg_bytes = 0;
1648  for (ValueType param_type : sig->parameters()) {
1649  liftoff::Store(this, esp, arg_bytes, *args++, param_type);
1650  arg_bytes += ValueTypes::MemSize(param_type);
1651  }
1652  DCHECK_LE(arg_bytes, stack_bytes);
1653 
1654  constexpr Register kScratch = eax;
1655  constexpr Register kArgumentBuffer = ecx;
1656  constexpr int kNumCCallArgs = 1;
1657  mov(kArgumentBuffer, esp);
1658  PrepareCallCFunction(kNumCCallArgs, kScratch);
1659 
1660  // Pass a pointer to the buffer with the arguments to the C function. ia32
1661  // does not use registers here, so push to the stack.
1662  mov(Operand(esp, 0), kArgumentBuffer);
1663 
1664  // Now call the C function.
1665  CallCFunction(ext_ref, kNumCCallArgs);
1666 
1667  // Move return value to the right register.
1668  const LiftoffRegister* next_result_reg = rets;
1669  if (sig->return_count() > 0) {
1670  DCHECK_EQ(1, sig->return_count());
1671  constexpr Register kReturnReg = eax;
1672  if (kReturnReg != next_result_reg->gp()) {
1673  Move(*next_result_reg, LiftoffRegister(kReturnReg), sig->GetReturn(0));
1674  }
1675  ++next_result_reg;
1676  }
1677 
1678  // Load potential output value from the buffer on the stack.
1679  if (out_argument_type != kWasmStmt) {
1680  liftoff::Load(this, *next_result_reg, esp, 0, out_argument_type);
1681  }
1682 
1683  add(esp, Immediate(stack_bytes));
1684 }
1685 
1686 void LiftoffAssembler::CallNativeWasmCode(Address addr) {
1687  wasm_call(addr, RelocInfo::WASM_CALL);
1688 }
1689 
1690 void LiftoffAssembler::CallIndirect(wasm::FunctionSig* sig,
1691  compiler::CallDescriptor* call_descriptor,
1692  Register target) {
1693  // Since we have more cache registers than parameter registers, the
1694  // {LiftoffCompiler} should always be able to place {target} in a register.
1695  DCHECK(target.is_valid());
1696  if (FLAG_untrusted_code_mitigations) {
1697  RetpolineCall(target);
1698  } else {
1699  call(target);
1700  }
1701 }
1702 
1703 void LiftoffAssembler::CallRuntimeStub(WasmCode::RuntimeStubId sid) {
1704  // A direct call to a wasm runtime stub defined in this module.
1705  // Just encode the stub index. This will be patched at relocation.
1706  wasm_call(static_cast<Address>(sid), RelocInfo::WASM_STUB_CALL);
1707 }
1708 
1709 void LiftoffAssembler::AllocateStackSlot(Register addr, uint32_t size) {
1710  sub(esp, Immediate(size));
1711  mov(addr, esp);
1712 }
1713 
1714 void LiftoffAssembler::DeallocateStackSlot(uint32_t size) {
1715  add(esp, Immediate(size));
1716 }
1717 
1718 void LiftoffStackSlots::Construct() {
1719  for (auto& slot : slots_) {
1720  const LiftoffAssembler::VarState& src = slot.src_;
1721  switch (src.loc()) {
1722  case LiftoffAssembler::VarState::kStack:
1723  if (src.type() == kWasmF64) {
1724  DCHECK_EQ(kLowWord, slot.half_);
1725  asm_->push(liftoff::GetHalfStackSlot(2 * slot.src_index_ - 1));
1726  }
1727  asm_->push(liftoff::GetHalfStackSlot(2 * slot.src_index_ -
1728  (slot.half_ == kLowWord ? 0 : 1)));
1729  break;
1730  case LiftoffAssembler::VarState::kRegister:
1731  if (src.type() == kWasmI64) {
1732  liftoff::push(
1733  asm_, slot.half_ == kLowWord ? src.reg().low() : src.reg().high(),
1734  kWasmI32);
1735  } else {
1736  liftoff::push(asm_, src.reg(), src.type());
1737  }
1738  break;
1739  case LiftoffAssembler::VarState::KIntConst:
1740  // The high word is the sign extension of the low word.
1741  asm_->push(Immediate(slot.half_ == kLowWord ? src.i32_const()
1742  : src.i32_const() >> 31));
1743  break;
1744  }
1745  }
1746 }
1747 
1748 #undef REQUIRE_CPU_FEATURE
1749 
1750 } // namespace wasm
1751 } // namespace internal
1752 } // namespace v8
1753 
1754 #endif // V8_WASM_BASELINE_IA32_LIFTOFF_ASSEMBLER_IA32_H_
Definition: libplatform.h:13