LCOV - code coverage report
Current view: top level - src/wasm/baseline/x64 - liftoff-assembler-x64.h (source / functions) Hit Total Coverage
Test: app.info Lines: 548 616 89.0 %
Date: 2019-01-20 Functions: 99 100 99.0 %

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

Generated by: LCOV version 1.10