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