11cb0ef41Sopenharmony_ci// Copyright 2017 the V8 project authors. All rights reserved.
21cb0ef41Sopenharmony_ci// Use of this source code is governed by a BSD-style license that can be
31cb0ef41Sopenharmony_ci// found in the LICENSE file.
41cb0ef41Sopenharmony_ci
51cb0ef41Sopenharmony_ci#include "src/wasm/baseline/liftoff-assembler.h"
61cb0ef41Sopenharmony_ci
71cb0ef41Sopenharmony_ci#include <sstream>
81cb0ef41Sopenharmony_ci
91cb0ef41Sopenharmony_ci#include "src/base/optional.h"
101cb0ef41Sopenharmony_ci#include "src/base/platform/wrappers.h"
111cb0ef41Sopenharmony_ci#include "src/codegen/assembler-inl.h"
121cb0ef41Sopenharmony_ci#include "src/codegen/macro-assembler-inl.h"
131cb0ef41Sopenharmony_ci#include "src/compiler/linkage.h"
141cb0ef41Sopenharmony_ci#include "src/compiler/wasm-compiler.h"
151cb0ef41Sopenharmony_ci#include "src/utils/ostreams.h"
161cb0ef41Sopenharmony_ci#include "src/wasm/baseline/liftoff-register.h"
171cb0ef41Sopenharmony_ci#include "src/wasm/function-body-decoder-impl.h"
181cb0ef41Sopenharmony_ci#include "src/wasm/object-access.h"
191cb0ef41Sopenharmony_ci#include "src/wasm/wasm-linkage.h"
201cb0ef41Sopenharmony_ci#include "src/wasm/wasm-opcodes.h"
211cb0ef41Sopenharmony_ci
221cb0ef41Sopenharmony_cinamespace v8 {
231cb0ef41Sopenharmony_cinamespace internal {
241cb0ef41Sopenharmony_cinamespace wasm {
251cb0ef41Sopenharmony_ci
261cb0ef41Sopenharmony_ciusing VarState = LiftoffAssembler::VarState;
271cb0ef41Sopenharmony_ciusing ValueKindSig = LiftoffAssembler::ValueKindSig;
281cb0ef41Sopenharmony_ci
291cb0ef41Sopenharmony_ciconstexpr ValueKind LiftoffAssembler::kPointerKind;
301cb0ef41Sopenharmony_ciconstexpr ValueKind LiftoffAssembler::kTaggedKind;
311cb0ef41Sopenharmony_ciconstexpr ValueKind LiftoffAssembler::kSmiKind;
321cb0ef41Sopenharmony_ci
331cb0ef41Sopenharmony_cinamespace {
341cb0ef41Sopenharmony_ci
351cb0ef41Sopenharmony_ciclass StackTransferRecipe {
361cb0ef41Sopenharmony_ci  struct RegisterMove {
371cb0ef41Sopenharmony_ci    LiftoffRegister src;
381cb0ef41Sopenharmony_ci    ValueKind kind;
391cb0ef41Sopenharmony_ci    constexpr RegisterMove(LiftoffRegister src, ValueKind kind)
401cb0ef41Sopenharmony_ci        : src(src), kind(kind) {}
411cb0ef41Sopenharmony_ci  };
421cb0ef41Sopenharmony_ci
431cb0ef41Sopenharmony_ci  struct RegisterLoad {
441cb0ef41Sopenharmony_ci    enum LoadKind : uint8_t {
451cb0ef41Sopenharmony_ci      kNop,           // no-op, used for high fp of a fp pair.
461cb0ef41Sopenharmony_ci      kConstant,      // load a constant value into a register.
471cb0ef41Sopenharmony_ci      kStack,         // fill a register from a stack slot.
481cb0ef41Sopenharmony_ci      kLowHalfStack,  // fill a register from the low half of a stack slot.
491cb0ef41Sopenharmony_ci      kHighHalfStack  // fill a register from the high half of a stack slot.
501cb0ef41Sopenharmony_ci    };
511cb0ef41Sopenharmony_ci
521cb0ef41Sopenharmony_ci    LoadKind load_kind;
531cb0ef41Sopenharmony_ci    ValueKind kind;
541cb0ef41Sopenharmony_ci    int32_t value;  // i32 constant value or stack offset, depending on kind.
551cb0ef41Sopenharmony_ci
561cb0ef41Sopenharmony_ci    // Named constructors.
571cb0ef41Sopenharmony_ci    static RegisterLoad Const(WasmValue constant) {
581cb0ef41Sopenharmony_ci      if (constant.type().kind() == kI32) {
591cb0ef41Sopenharmony_ci        return {kConstant, kI32, constant.to_i32()};
601cb0ef41Sopenharmony_ci      }
611cb0ef41Sopenharmony_ci      DCHECK_EQ(kI64, constant.type().kind());
621cb0ef41Sopenharmony_ci      int32_t i32_const = static_cast<int32_t>(constant.to_i64());
631cb0ef41Sopenharmony_ci      DCHECK_EQ(constant.to_i64(), i32_const);
641cb0ef41Sopenharmony_ci      return {kConstant, kI64, i32_const};
651cb0ef41Sopenharmony_ci    }
661cb0ef41Sopenharmony_ci    static RegisterLoad Stack(int32_t offset, ValueKind kind) {
671cb0ef41Sopenharmony_ci      return {kStack, kind, offset};
681cb0ef41Sopenharmony_ci    }
691cb0ef41Sopenharmony_ci    static RegisterLoad HalfStack(int32_t offset, RegPairHalf half) {
701cb0ef41Sopenharmony_ci      return {half == kLowWord ? kLowHalfStack : kHighHalfStack, kI32, offset};
711cb0ef41Sopenharmony_ci    }
721cb0ef41Sopenharmony_ci    static RegisterLoad Nop() {
731cb0ef41Sopenharmony_ci      // ValueKind does not matter.
741cb0ef41Sopenharmony_ci      return {kNop, kI32, 0};
751cb0ef41Sopenharmony_ci    }
761cb0ef41Sopenharmony_ci
771cb0ef41Sopenharmony_ci   private:
781cb0ef41Sopenharmony_ci    RegisterLoad(LoadKind load_kind, ValueKind kind, int32_t value)
791cb0ef41Sopenharmony_ci        : load_kind(load_kind), kind(kind), value(value) {}
801cb0ef41Sopenharmony_ci  };
811cb0ef41Sopenharmony_ci
821cb0ef41Sopenharmony_ci public:
831cb0ef41Sopenharmony_ci  explicit StackTransferRecipe(LiftoffAssembler* wasm_asm) : asm_(wasm_asm) {}
841cb0ef41Sopenharmony_ci  StackTransferRecipe(const StackTransferRecipe&) = delete;
851cb0ef41Sopenharmony_ci  StackTransferRecipe& operator=(const StackTransferRecipe&) = delete;
861cb0ef41Sopenharmony_ci  ~StackTransferRecipe() { Execute(); }
871cb0ef41Sopenharmony_ci
881cb0ef41Sopenharmony_ci  void Execute() {
891cb0ef41Sopenharmony_ci    // First, execute register moves. Then load constants and stack values into
901cb0ef41Sopenharmony_ci    // registers.
911cb0ef41Sopenharmony_ci    ExecuteMoves();
921cb0ef41Sopenharmony_ci    DCHECK(move_dst_regs_.is_empty());
931cb0ef41Sopenharmony_ci    ExecuteLoads();
941cb0ef41Sopenharmony_ci    DCHECK(load_dst_regs_.is_empty());
951cb0ef41Sopenharmony_ci  }
961cb0ef41Sopenharmony_ci
971cb0ef41Sopenharmony_ci  V8_INLINE void TransferStackSlot(const VarState& dst, const VarState& src) {
981cb0ef41Sopenharmony_ci    DCHECK(CheckCompatibleStackSlotTypes(dst.kind(), src.kind()));
991cb0ef41Sopenharmony_ci    if (dst.is_reg()) {
1001cb0ef41Sopenharmony_ci      LoadIntoRegister(dst.reg(), src, src.offset());
1011cb0ef41Sopenharmony_ci      return;
1021cb0ef41Sopenharmony_ci    }
1031cb0ef41Sopenharmony_ci    if (dst.is_const()) {
1041cb0ef41Sopenharmony_ci      DCHECK_EQ(dst.i32_const(), src.i32_const());
1051cb0ef41Sopenharmony_ci      return;
1061cb0ef41Sopenharmony_ci    }
1071cb0ef41Sopenharmony_ci    DCHECK(dst.is_stack());
1081cb0ef41Sopenharmony_ci    switch (src.loc()) {
1091cb0ef41Sopenharmony_ci      case VarState::kStack:
1101cb0ef41Sopenharmony_ci        if (src.offset() != dst.offset()) {
1111cb0ef41Sopenharmony_ci          asm_->MoveStackValue(dst.offset(), src.offset(), src.kind());
1121cb0ef41Sopenharmony_ci        }
1131cb0ef41Sopenharmony_ci        break;
1141cb0ef41Sopenharmony_ci      case VarState::kRegister:
1151cb0ef41Sopenharmony_ci        asm_->Spill(dst.offset(), src.reg(), src.kind());
1161cb0ef41Sopenharmony_ci        break;
1171cb0ef41Sopenharmony_ci      case VarState::kIntConst:
1181cb0ef41Sopenharmony_ci        asm_->Spill(dst.offset(), src.constant());
1191cb0ef41Sopenharmony_ci        break;
1201cb0ef41Sopenharmony_ci    }
1211cb0ef41Sopenharmony_ci  }
1221cb0ef41Sopenharmony_ci
1231cb0ef41Sopenharmony_ci  V8_INLINE void LoadIntoRegister(LiftoffRegister dst,
1241cb0ef41Sopenharmony_ci                                  const LiftoffAssembler::VarState& src,
1251cb0ef41Sopenharmony_ci                                  uint32_t src_offset) {
1261cb0ef41Sopenharmony_ci    switch (src.loc()) {
1271cb0ef41Sopenharmony_ci      case VarState::kStack:
1281cb0ef41Sopenharmony_ci        LoadStackSlot(dst, src_offset, src.kind());
1291cb0ef41Sopenharmony_ci        break;
1301cb0ef41Sopenharmony_ci      case VarState::kRegister:
1311cb0ef41Sopenharmony_ci        DCHECK_EQ(dst.reg_class(), src.reg_class());
1321cb0ef41Sopenharmony_ci        if (dst != src.reg()) MoveRegister(dst, src.reg(), src.kind());
1331cb0ef41Sopenharmony_ci        break;
1341cb0ef41Sopenharmony_ci      case VarState::kIntConst:
1351cb0ef41Sopenharmony_ci        LoadConstant(dst, src.constant());
1361cb0ef41Sopenharmony_ci        break;
1371cb0ef41Sopenharmony_ci    }
1381cb0ef41Sopenharmony_ci  }
1391cb0ef41Sopenharmony_ci
1401cb0ef41Sopenharmony_ci  void LoadI64HalfIntoRegister(LiftoffRegister dst,
1411cb0ef41Sopenharmony_ci                               const LiftoffAssembler::VarState& src,
1421cb0ef41Sopenharmony_ci                               int offset, RegPairHalf half) {
1431cb0ef41Sopenharmony_ci    // Use CHECK such that the remaining code is statically dead if
1441cb0ef41Sopenharmony_ci    // {kNeedI64RegPair} is false.
1451cb0ef41Sopenharmony_ci    CHECK(kNeedI64RegPair);
1461cb0ef41Sopenharmony_ci    DCHECK_EQ(kI64, src.kind());
1471cb0ef41Sopenharmony_ci    switch (src.loc()) {
1481cb0ef41Sopenharmony_ci      case VarState::kStack:
1491cb0ef41Sopenharmony_ci        LoadI64HalfStackSlot(dst, offset, half);
1501cb0ef41Sopenharmony_ci        break;
1511cb0ef41Sopenharmony_ci      case VarState::kRegister: {
1521cb0ef41Sopenharmony_ci        LiftoffRegister src_half =
1531cb0ef41Sopenharmony_ci            half == kLowWord ? src.reg().low() : src.reg().high();
1541cb0ef41Sopenharmony_ci        if (dst != src_half) MoveRegister(dst, src_half, kI32);
1551cb0ef41Sopenharmony_ci        break;
1561cb0ef41Sopenharmony_ci      }
1571cb0ef41Sopenharmony_ci      case VarState::kIntConst:
1581cb0ef41Sopenharmony_ci        int32_t value = src.i32_const();
1591cb0ef41Sopenharmony_ci        // The high word is the sign extension of the low word.
1601cb0ef41Sopenharmony_ci        if (half == kHighWord) value = value >> 31;
1611cb0ef41Sopenharmony_ci        LoadConstant(dst, WasmValue(value));
1621cb0ef41Sopenharmony_ci        break;
1631cb0ef41Sopenharmony_ci    }
1641cb0ef41Sopenharmony_ci  }
1651cb0ef41Sopenharmony_ci
1661cb0ef41Sopenharmony_ci  void MoveRegister(LiftoffRegister dst, LiftoffRegister src, ValueKind kind) {
1671cb0ef41Sopenharmony_ci    DCHECK_NE(dst, src);
1681cb0ef41Sopenharmony_ci    DCHECK_EQ(dst.reg_class(), src.reg_class());
1691cb0ef41Sopenharmony_ci    DCHECK_EQ(reg_class_for(kind), src.reg_class());
1701cb0ef41Sopenharmony_ci    if (src.is_gp_pair()) {
1711cb0ef41Sopenharmony_ci      DCHECK_EQ(kI64, kind);
1721cb0ef41Sopenharmony_ci      if (dst.low() != src.low()) MoveRegister(dst.low(), src.low(), kI32);
1731cb0ef41Sopenharmony_ci      if (dst.high() != src.high()) MoveRegister(dst.high(), src.high(), kI32);
1741cb0ef41Sopenharmony_ci      return;
1751cb0ef41Sopenharmony_ci    }
1761cb0ef41Sopenharmony_ci    if (src.is_fp_pair()) {
1771cb0ef41Sopenharmony_ci      DCHECK_EQ(kS128, kind);
1781cb0ef41Sopenharmony_ci      if (dst.low() != src.low()) {
1791cb0ef41Sopenharmony_ci        MoveRegister(dst.low(), src.low(), kF64);
1801cb0ef41Sopenharmony_ci        MoveRegister(dst.high(), src.high(), kF64);
1811cb0ef41Sopenharmony_ci      }
1821cb0ef41Sopenharmony_ci      return;
1831cb0ef41Sopenharmony_ci    }
1841cb0ef41Sopenharmony_ci    if (move_dst_regs_.has(dst)) {
1851cb0ef41Sopenharmony_ci      DCHECK_EQ(register_move(dst)->src, src);
1861cb0ef41Sopenharmony_ci      // Non-fp registers can only occur with the exact same type.
1871cb0ef41Sopenharmony_ci      DCHECK_IMPLIES(!dst.is_fp(), register_move(dst)->kind == kind);
1881cb0ef41Sopenharmony_ci      // It can happen that one fp register holds both the f32 zero and the f64
1891cb0ef41Sopenharmony_ci      // zero, as the initial value for local variables. Move the value as f64
1901cb0ef41Sopenharmony_ci      // in that case.
1911cb0ef41Sopenharmony_ci      if (kind == kF64) register_move(dst)->kind = kF64;
1921cb0ef41Sopenharmony_ci      return;
1931cb0ef41Sopenharmony_ci    }
1941cb0ef41Sopenharmony_ci    move_dst_regs_.set(dst);
1951cb0ef41Sopenharmony_ci    ++*src_reg_use_count(src);
1961cb0ef41Sopenharmony_ci    *register_move(dst) = {src, kind};
1971cb0ef41Sopenharmony_ci  }
1981cb0ef41Sopenharmony_ci
1991cb0ef41Sopenharmony_ci  void LoadConstant(LiftoffRegister dst, WasmValue value) {
2001cb0ef41Sopenharmony_ci    DCHECK(!load_dst_regs_.has(dst));
2011cb0ef41Sopenharmony_ci    load_dst_regs_.set(dst);
2021cb0ef41Sopenharmony_ci    if (dst.is_gp_pair()) {
2031cb0ef41Sopenharmony_ci      DCHECK_EQ(kI64, value.type().kind());
2041cb0ef41Sopenharmony_ci      int64_t i64 = value.to_i64();
2051cb0ef41Sopenharmony_ci      *register_load(dst.low()) =
2061cb0ef41Sopenharmony_ci          RegisterLoad::Const(WasmValue(static_cast<int32_t>(i64)));
2071cb0ef41Sopenharmony_ci      *register_load(dst.high()) =
2081cb0ef41Sopenharmony_ci          RegisterLoad::Const(WasmValue(static_cast<int32_t>(i64 >> 32)));
2091cb0ef41Sopenharmony_ci    } else {
2101cb0ef41Sopenharmony_ci      *register_load(dst) = RegisterLoad::Const(value);
2111cb0ef41Sopenharmony_ci    }
2121cb0ef41Sopenharmony_ci  }
2131cb0ef41Sopenharmony_ci
2141cb0ef41Sopenharmony_ci  void LoadStackSlot(LiftoffRegister dst, uint32_t stack_offset,
2151cb0ef41Sopenharmony_ci                     ValueKind kind) {
2161cb0ef41Sopenharmony_ci    if (load_dst_regs_.has(dst)) {
2171cb0ef41Sopenharmony_ci      // It can happen that we spilled the same register to different stack
2181cb0ef41Sopenharmony_ci      // slots, and then we reload them later into the same dst register.
2191cb0ef41Sopenharmony_ci      // In that case, it is enough to load one of the stack slots.
2201cb0ef41Sopenharmony_ci      return;
2211cb0ef41Sopenharmony_ci    }
2221cb0ef41Sopenharmony_ci    load_dst_regs_.set(dst);
2231cb0ef41Sopenharmony_ci    if (dst.is_gp_pair()) {
2241cb0ef41Sopenharmony_ci      DCHECK_EQ(kI64, kind);
2251cb0ef41Sopenharmony_ci      *register_load(dst.low()) =
2261cb0ef41Sopenharmony_ci          RegisterLoad::HalfStack(stack_offset, kLowWord);
2271cb0ef41Sopenharmony_ci      *register_load(dst.high()) =
2281cb0ef41Sopenharmony_ci          RegisterLoad::HalfStack(stack_offset, kHighWord);
2291cb0ef41Sopenharmony_ci    } else if (dst.is_fp_pair()) {
2301cb0ef41Sopenharmony_ci      DCHECK_EQ(kS128, kind);
2311cb0ef41Sopenharmony_ci      // Only need register_load for low_gp since we load 128 bits at one go.
2321cb0ef41Sopenharmony_ci      // Both low and high need to be set in load_dst_regs_ but when iterating
2331cb0ef41Sopenharmony_ci      // over it, both low and high will be cleared, so we won't load twice.
2341cb0ef41Sopenharmony_ci      *register_load(dst.low()) = RegisterLoad::Stack(stack_offset, kind);
2351cb0ef41Sopenharmony_ci      *register_load(dst.high()) = RegisterLoad::Nop();
2361cb0ef41Sopenharmony_ci    } else {
2371cb0ef41Sopenharmony_ci      *register_load(dst) = RegisterLoad::Stack(stack_offset, kind);
2381cb0ef41Sopenharmony_ci    }
2391cb0ef41Sopenharmony_ci  }
2401cb0ef41Sopenharmony_ci
2411cb0ef41Sopenharmony_ci  void LoadI64HalfStackSlot(LiftoffRegister dst, int offset, RegPairHalf half) {
2421cb0ef41Sopenharmony_ci    if (load_dst_regs_.has(dst)) {
2431cb0ef41Sopenharmony_ci      // It can happen that we spilled the same register to different stack
2441cb0ef41Sopenharmony_ci      // slots, and then we reload them later into the same dst register.
2451cb0ef41Sopenharmony_ci      // In that case, it is enough to load one of the stack slots.
2461cb0ef41Sopenharmony_ci      return;
2471cb0ef41Sopenharmony_ci    }
2481cb0ef41Sopenharmony_ci    load_dst_regs_.set(dst);
2491cb0ef41Sopenharmony_ci    *register_load(dst) = RegisterLoad::HalfStack(offset, half);
2501cb0ef41Sopenharmony_ci  }
2511cb0ef41Sopenharmony_ci
2521cb0ef41Sopenharmony_ci private:
2531cb0ef41Sopenharmony_ci  using MovesStorage =
2541cb0ef41Sopenharmony_ci      std::aligned_storage<kAfterMaxLiftoffRegCode * sizeof(RegisterMove),
2551cb0ef41Sopenharmony_ci                           alignof(RegisterMove)>::type;
2561cb0ef41Sopenharmony_ci  using LoadsStorage =
2571cb0ef41Sopenharmony_ci      std::aligned_storage<kAfterMaxLiftoffRegCode * sizeof(RegisterLoad),
2581cb0ef41Sopenharmony_ci                           alignof(RegisterLoad)>::type;
2591cb0ef41Sopenharmony_ci
2601cb0ef41Sopenharmony_ci  ASSERT_TRIVIALLY_COPYABLE(RegisterMove);
2611cb0ef41Sopenharmony_ci  ASSERT_TRIVIALLY_COPYABLE(RegisterLoad);
2621cb0ef41Sopenharmony_ci
2631cb0ef41Sopenharmony_ci  MovesStorage register_moves_;  // uninitialized
2641cb0ef41Sopenharmony_ci  LoadsStorage register_loads_;  // uninitialized
2651cb0ef41Sopenharmony_ci  int src_reg_use_count_[kAfterMaxLiftoffRegCode] = {0};
2661cb0ef41Sopenharmony_ci  LiftoffRegList move_dst_regs_;
2671cb0ef41Sopenharmony_ci  LiftoffRegList load_dst_regs_;
2681cb0ef41Sopenharmony_ci  LiftoffAssembler* const asm_;
2691cb0ef41Sopenharmony_ci
2701cb0ef41Sopenharmony_ci  RegisterMove* register_move(LiftoffRegister reg) {
2711cb0ef41Sopenharmony_ci    return reinterpret_cast<RegisterMove*>(&register_moves_) +
2721cb0ef41Sopenharmony_ci           reg.liftoff_code();
2731cb0ef41Sopenharmony_ci  }
2741cb0ef41Sopenharmony_ci  RegisterLoad* register_load(LiftoffRegister reg) {
2751cb0ef41Sopenharmony_ci    return reinterpret_cast<RegisterLoad*>(&register_loads_) +
2761cb0ef41Sopenharmony_ci           reg.liftoff_code();
2771cb0ef41Sopenharmony_ci  }
2781cb0ef41Sopenharmony_ci  int* src_reg_use_count(LiftoffRegister reg) {
2791cb0ef41Sopenharmony_ci    return src_reg_use_count_ + reg.liftoff_code();
2801cb0ef41Sopenharmony_ci  }
2811cb0ef41Sopenharmony_ci
2821cb0ef41Sopenharmony_ci  void ExecuteMove(LiftoffRegister dst) {
2831cb0ef41Sopenharmony_ci    RegisterMove* move = register_move(dst);
2841cb0ef41Sopenharmony_ci    DCHECK_EQ(0, *src_reg_use_count(dst));
2851cb0ef41Sopenharmony_ci    asm_->Move(dst, move->src, move->kind);
2861cb0ef41Sopenharmony_ci    ClearExecutedMove(dst);
2871cb0ef41Sopenharmony_ci  }
2881cb0ef41Sopenharmony_ci
2891cb0ef41Sopenharmony_ci  void ClearExecutedMove(LiftoffRegister dst) {
2901cb0ef41Sopenharmony_ci    DCHECK(move_dst_regs_.has(dst));
2911cb0ef41Sopenharmony_ci    move_dst_regs_.clear(dst);
2921cb0ef41Sopenharmony_ci    RegisterMove* move = register_move(dst);
2931cb0ef41Sopenharmony_ci    DCHECK_LT(0, *src_reg_use_count(move->src));
2941cb0ef41Sopenharmony_ci    if (--*src_reg_use_count(move->src)) return;
2951cb0ef41Sopenharmony_ci    // src count dropped to zero. If this is a destination register, execute
2961cb0ef41Sopenharmony_ci    // that move now.
2971cb0ef41Sopenharmony_ci    if (!move_dst_regs_.has(move->src)) return;
2981cb0ef41Sopenharmony_ci    ExecuteMove(move->src);
2991cb0ef41Sopenharmony_ci  }
3001cb0ef41Sopenharmony_ci
3011cb0ef41Sopenharmony_ci  void ExecuteMoves() {
3021cb0ef41Sopenharmony_ci    // Execute all moves whose {dst} is not being used as src in another move.
3031cb0ef41Sopenharmony_ci    // If any src count drops to zero, also (transitively) execute the
3041cb0ef41Sopenharmony_ci    // corresponding move to that register.
3051cb0ef41Sopenharmony_ci    for (LiftoffRegister dst : move_dst_regs_) {
3061cb0ef41Sopenharmony_ci      // Check if already handled via transitivity in {ClearExecutedMove}.
3071cb0ef41Sopenharmony_ci      if (!move_dst_regs_.has(dst)) continue;
3081cb0ef41Sopenharmony_ci      if (*src_reg_use_count(dst)) continue;
3091cb0ef41Sopenharmony_ci      ExecuteMove(dst);
3101cb0ef41Sopenharmony_ci    }
3111cb0ef41Sopenharmony_ci
3121cb0ef41Sopenharmony_ci    // All remaining moves are parts of a cycle. Just spill the first one, then
3131cb0ef41Sopenharmony_ci    // process all remaining moves in that cycle. Repeat for all cycles.
3141cb0ef41Sopenharmony_ci    int last_spill_offset = asm_->TopSpillOffset();
3151cb0ef41Sopenharmony_ci    while (!move_dst_regs_.is_empty()) {
3161cb0ef41Sopenharmony_ci      // TODO(clemensb): Use an unused register if available.
3171cb0ef41Sopenharmony_ci      LiftoffRegister dst = move_dst_regs_.GetFirstRegSet();
3181cb0ef41Sopenharmony_ci      RegisterMove* move = register_move(dst);
3191cb0ef41Sopenharmony_ci      last_spill_offset += LiftoffAssembler::SlotSizeForType(move->kind);
3201cb0ef41Sopenharmony_ci      LiftoffRegister spill_reg = move->src;
3211cb0ef41Sopenharmony_ci      asm_->Spill(last_spill_offset, spill_reg, move->kind);
3221cb0ef41Sopenharmony_ci      // Remember to reload into the destination register later.
3231cb0ef41Sopenharmony_ci      LoadStackSlot(dst, last_spill_offset, move->kind);
3241cb0ef41Sopenharmony_ci      ClearExecutedMove(dst);
3251cb0ef41Sopenharmony_ci    }
3261cb0ef41Sopenharmony_ci  }
3271cb0ef41Sopenharmony_ci
3281cb0ef41Sopenharmony_ci  void ExecuteLoads() {
3291cb0ef41Sopenharmony_ci    for (LiftoffRegister dst : load_dst_regs_) {
3301cb0ef41Sopenharmony_ci      RegisterLoad* load = register_load(dst);
3311cb0ef41Sopenharmony_ci      switch (load->load_kind) {
3321cb0ef41Sopenharmony_ci        case RegisterLoad::kNop:
3331cb0ef41Sopenharmony_ci          break;
3341cb0ef41Sopenharmony_ci        case RegisterLoad::kConstant:
3351cb0ef41Sopenharmony_ci          asm_->LoadConstant(dst, load->kind == kI64
3361cb0ef41Sopenharmony_ci                                      ? WasmValue(int64_t{load->value})
3371cb0ef41Sopenharmony_ci                                      : WasmValue(int32_t{load->value}));
3381cb0ef41Sopenharmony_ci          break;
3391cb0ef41Sopenharmony_ci        case RegisterLoad::kStack:
3401cb0ef41Sopenharmony_ci          if (kNeedS128RegPair && load->kind == kS128) {
3411cb0ef41Sopenharmony_ci            asm_->Fill(LiftoffRegister::ForFpPair(dst.fp()), load->value,
3421cb0ef41Sopenharmony_ci                       load->kind);
3431cb0ef41Sopenharmony_ci          } else {
3441cb0ef41Sopenharmony_ci            asm_->Fill(dst, load->value, load->kind);
3451cb0ef41Sopenharmony_ci          }
3461cb0ef41Sopenharmony_ci          break;
3471cb0ef41Sopenharmony_ci        case RegisterLoad::kLowHalfStack:
3481cb0ef41Sopenharmony_ci          // Half of a register pair, {dst} must be a gp register.
3491cb0ef41Sopenharmony_ci          asm_->FillI64Half(dst.gp(), load->value, kLowWord);
3501cb0ef41Sopenharmony_ci          break;
3511cb0ef41Sopenharmony_ci        case RegisterLoad::kHighHalfStack:
3521cb0ef41Sopenharmony_ci          // Half of a register pair, {dst} must be a gp register.
3531cb0ef41Sopenharmony_ci          asm_->FillI64Half(dst.gp(), load->value, kHighWord);
3541cb0ef41Sopenharmony_ci          break;
3551cb0ef41Sopenharmony_ci      }
3561cb0ef41Sopenharmony_ci    }
3571cb0ef41Sopenharmony_ci    load_dst_regs_ = {};
3581cb0ef41Sopenharmony_ci  }
3591cb0ef41Sopenharmony_ci};
3601cb0ef41Sopenharmony_ci
3611cb0ef41Sopenharmony_ciclass RegisterReuseMap {
3621cb0ef41Sopenharmony_ci public:
3631cb0ef41Sopenharmony_ci  void Add(LiftoffRegister src, LiftoffRegister dst) {
3641cb0ef41Sopenharmony_ci    if (auto previous = Lookup(src)) {
3651cb0ef41Sopenharmony_ci      DCHECK_EQ(previous, dst);
3661cb0ef41Sopenharmony_ci      return;
3671cb0ef41Sopenharmony_ci    }
3681cb0ef41Sopenharmony_ci    map_.emplace_back(src);
3691cb0ef41Sopenharmony_ci    map_.emplace_back(dst);
3701cb0ef41Sopenharmony_ci  }
3711cb0ef41Sopenharmony_ci
3721cb0ef41Sopenharmony_ci  base::Optional<LiftoffRegister> Lookup(LiftoffRegister src) {
3731cb0ef41Sopenharmony_ci    for (auto it = map_.begin(), end = map_.end(); it != end; it += 2) {
3741cb0ef41Sopenharmony_ci      if (it->is_gp_pair() == src.is_gp_pair() &&
3751cb0ef41Sopenharmony_ci          it->is_fp_pair() == src.is_fp_pair() && *it == src)
3761cb0ef41Sopenharmony_ci        return *(it + 1);
3771cb0ef41Sopenharmony_ci    }
3781cb0ef41Sopenharmony_ci    return {};
3791cb0ef41Sopenharmony_ci  }
3801cb0ef41Sopenharmony_ci
3811cb0ef41Sopenharmony_ci private:
3821cb0ef41Sopenharmony_ci  // {map_} holds pairs of <src, dst>.
3831cb0ef41Sopenharmony_ci  base::SmallVector<LiftoffRegister, 8> map_;
3841cb0ef41Sopenharmony_ci};
3851cb0ef41Sopenharmony_ci
3861cb0ef41Sopenharmony_cienum MergeKeepStackSlots : bool {
3871cb0ef41Sopenharmony_ci  kKeepStackSlots = true,
3881cb0ef41Sopenharmony_ci  kTurnStackSlotsIntoRegisters = false
3891cb0ef41Sopenharmony_ci};
3901cb0ef41Sopenharmony_cienum MergeAllowConstants : bool {
3911cb0ef41Sopenharmony_ci  kConstantsAllowed = true,
3921cb0ef41Sopenharmony_ci  kConstantsNotAllowed = false
3931cb0ef41Sopenharmony_ci};
3941cb0ef41Sopenharmony_cienum MergeAllowRegisters : bool {
3951cb0ef41Sopenharmony_ci  kRegistersAllowed = true,
3961cb0ef41Sopenharmony_ci  kRegistersNotAllowed = false
3971cb0ef41Sopenharmony_ci};
3981cb0ef41Sopenharmony_cienum ReuseRegisters : bool {
3991cb0ef41Sopenharmony_ci  kReuseRegisters = true,
4001cb0ef41Sopenharmony_ci  kNoReuseRegisters = false
4011cb0ef41Sopenharmony_ci};
4021cb0ef41Sopenharmony_civoid InitMergeRegion(LiftoffAssembler::CacheState* state,
4031cb0ef41Sopenharmony_ci                     const VarState* source, VarState* target, uint32_t count,
4041cb0ef41Sopenharmony_ci                     MergeKeepStackSlots keep_stack_slots,
4051cb0ef41Sopenharmony_ci                     MergeAllowConstants allow_constants,
4061cb0ef41Sopenharmony_ci                     MergeAllowRegisters allow_registers,
4071cb0ef41Sopenharmony_ci                     ReuseRegisters reuse_registers, LiftoffRegList used_regs) {
4081cb0ef41Sopenharmony_ci  RegisterReuseMap register_reuse_map;
4091cb0ef41Sopenharmony_ci  for (const VarState* source_end = source + count; source < source_end;
4101cb0ef41Sopenharmony_ci       ++source, ++target) {
4111cb0ef41Sopenharmony_ci    if ((source->is_stack() && keep_stack_slots) ||
4121cb0ef41Sopenharmony_ci        (source->is_const() && allow_constants)) {
4131cb0ef41Sopenharmony_ci      *target = *source;
4141cb0ef41Sopenharmony_ci      continue;
4151cb0ef41Sopenharmony_ci    }
4161cb0ef41Sopenharmony_ci    base::Optional<LiftoffRegister> reg;
4171cb0ef41Sopenharmony_ci    if (allow_registers) {
4181cb0ef41Sopenharmony_ci      // First try: Keep the same register, if it's free.
4191cb0ef41Sopenharmony_ci      if (source->is_reg() && state->is_free(source->reg())) {
4201cb0ef41Sopenharmony_ci        reg = source->reg();
4211cb0ef41Sopenharmony_ci      }
4221cb0ef41Sopenharmony_ci      // Second try: Use the same register we used before (if we reuse
4231cb0ef41Sopenharmony_ci      // registers).
4241cb0ef41Sopenharmony_ci      if (!reg && reuse_registers) {
4251cb0ef41Sopenharmony_ci        reg = register_reuse_map.Lookup(source->reg());
4261cb0ef41Sopenharmony_ci      }
4271cb0ef41Sopenharmony_ci      // Third try: Use any free register.
4281cb0ef41Sopenharmony_ci      RegClass rc = reg_class_for(source->kind());
4291cb0ef41Sopenharmony_ci      if (!reg && state->has_unused_register(rc, used_regs)) {
4301cb0ef41Sopenharmony_ci        reg = state->unused_register(rc, used_regs);
4311cb0ef41Sopenharmony_ci      }
4321cb0ef41Sopenharmony_ci    }
4331cb0ef41Sopenharmony_ci    if (!reg) {
4341cb0ef41Sopenharmony_ci      // No free register; make this a stack slot.
4351cb0ef41Sopenharmony_ci      *target = VarState(source->kind(), source->offset());
4361cb0ef41Sopenharmony_ci      continue;
4371cb0ef41Sopenharmony_ci    }
4381cb0ef41Sopenharmony_ci    if (reuse_registers) register_reuse_map.Add(source->reg(), *reg);
4391cb0ef41Sopenharmony_ci    state->inc_used(*reg);
4401cb0ef41Sopenharmony_ci    *target = VarState(source->kind(), *reg, source->offset());
4411cb0ef41Sopenharmony_ci  }
4421cb0ef41Sopenharmony_ci}
4431cb0ef41Sopenharmony_ci
4441cb0ef41Sopenharmony_ci}  // namespace
4451cb0ef41Sopenharmony_ci
4461cb0ef41Sopenharmony_ci// TODO(clemensb): Don't copy the full parent state (this makes us N^2).
4471cb0ef41Sopenharmony_civoid LiftoffAssembler::CacheState::InitMerge(const CacheState& source,
4481cb0ef41Sopenharmony_ci                                             uint32_t num_locals,
4491cb0ef41Sopenharmony_ci                                             uint32_t arity,
4501cb0ef41Sopenharmony_ci                                             uint32_t stack_depth) {
4511cb0ef41Sopenharmony_ci  // |------locals------|---(in between)----|--(discarded)--|----merge----|
4521cb0ef41Sopenharmony_ci  //  <-- num_locals --> <-- stack_depth -->^stack_base      <-- arity -->
4531cb0ef41Sopenharmony_ci
4541cb0ef41Sopenharmony_ci  if (source.cached_instance != no_reg) {
4551cb0ef41Sopenharmony_ci    SetInstanceCacheRegister(source.cached_instance);
4561cb0ef41Sopenharmony_ci  }
4571cb0ef41Sopenharmony_ci
4581cb0ef41Sopenharmony_ci  if (source.cached_mem_start != no_reg) {
4591cb0ef41Sopenharmony_ci    SetMemStartCacheRegister(source.cached_mem_start);
4601cb0ef41Sopenharmony_ci  }
4611cb0ef41Sopenharmony_ci
4621cb0ef41Sopenharmony_ci  uint32_t stack_base = stack_depth + num_locals;
4631cb0ef41Sopenharmony_ci  uint32_t target_height = stack_base + arity;
4641cb0ef41Sopenharmony_ci  uint32_t discarded = source.stack_height() - target_height;
4651cb0ef41Sopenharmony_ci  DCHECK(stack_state.empty());
4661cb0ef41Sopenharmony_ci
4671cb0ef41Sopenharmony_ci  DCHECK_GE(source.stack_height(), stack_base);
4681cb0ef41Sopenharmony_ci  stack_state.resize_no_init(target_height);
4691cb0ef41Sopenharmony_ci
4701cb0ef41Sopenharmony_ci  const VarState* source_begin = source.stack_state.data();
4711cb0ef41Sopenharmony_ci  VarState* target_begin = stack_state.data();
4721cb0ef41Sopenharmony_ci
4731cb0ef41Sopenharmony_ci  // Try to keep locals and the merge region in their registers. Register used
4741cb0ef41Sopenharmony_ci  // multiple times need to be copied to another free register. Compute the list
4751cb0ef41Sopenharmony_ci  // of used registers.
4761cb0ef41Sopenharmony_ci  LiftoffRegList used_regs;
4771cb0ef41Sopenharmony_ci  for (auto& src : base::VectorOf(source_begin, num_locals)) {
4781cb0ef41Sopenharmony_ci    if (src.is_reg()) used_regs.set(src.reg());
4791cb0ef41Sopenharmony_ci  }
4801cb0ef41Sopenharmony_ci  // If there is more than one operand in the merge region, a stack-to-stack
4811cb0ef41Sopenharmony_ci  // move can interfere with a register reload, which would not be handled
4821cb0ef41Sopenharmony_ci  // correctly by the StackTransferRecipe. To avoid this, spill all registers in
4831cb0ef41Sopenharmony_ci  // this region.
4841cb0ef41Sopenharmony_ci  MergeAllowRegisters allow_registers =
4851cb0ef41Sopenharmony_ci      arity <= 1 ? kRegistersAllowed : kRegistersNotAllowed;
4861cb0ef41Sopenharmony_ci  if (allow_registers) {
4871cb0ef41Sopenharmony_ci    for (auto& src :
4881cb0ef41Sopenharmony_ci         base::VectorOf(source_begin + stack_base + discarded, arity)) {
4891cb0ef41Sopenharmony_ci      if (src.is_reg()) used_regs.set(src.reg());
4901cb0ef41Sopenharmony_ci    }
4911cb0ef41Sopenharmony_ci  }
4921cb0ef41Sopenharmony_ci
4931cb0ef41Sopenharmony_ci  // Initialize the merge region. If this region moves, try to turn stack slots
4941cb0ef41Sopenharmony_ci  // into registers since we need to load the value anyways.
4951cb0ef41Sopenharmony_ci  MergeKeepStackSlots keep_merge_stack_slots =
4961cb0ef41Sopenharmony_ci      discarded == 0 ? kKeepStackSlots : kTurnStackSlotsIntoRegisters;
4971cb0ef41Sopenharmony_ci  InitMergeRegion(this, source_begin + stack_base + discarded,
4981cb0ef41Sopenharmony_ci                  target_begin + stack_base, arity, keep_merge_stack_slots,
4991cb0ef41Sopenharmony_ci                  kConstantsNotAllowed, allow_registers, kNoReuseRegisters,
5001cb0ef41Sopenharmony_ci                  used_regs);
5011cb0ef41Sopenharmony_ci  // Shift spill offsets down to keep slots contiguous.
5021cb0ef41Sopenharmony_ci  int offset = stack_base == 0 ? StaticStackFrameSize()
5031cb0ef41Sopenharmony_ci                               : source.stack_state[stack_base - 1].offset();
5041cb0ef41Sopenharmony_ci  auto merge_region = base::VectorOf(target_begin + stack_base, arity);
5051cb0ef41Sopenharmony_ci  for (VarState& var : merge_region) {
5061cb0ef41Sopenharmony_ci    offset = LiftoffAssembler::NextSpillOffset(var.kind(), offset);
5071cb0ef41Sopenharmony_ci    var.set_offset(offset);
5081cb0ef41Sopenharmony_ci  }
5091cb0ef41Sopenharmony_ci
5101cb0ef41Sopenharmony_ci  // Initialize the locals region. Here, stack slots stay stack slots (because
5111cb0ef41Sopenharmony_ci  // they do not move). Try to keep register in registers, but avoid duplicates.
5121cb0ef41Sopenharmony_ci  InitMergeRegion(this, source_begin, target_begin, num_locals, kKeepStackSlots,
5131cb0ef41Sopenharmony_ci                  kConstantsNotAllowed, kRegistersAllowed, kNoReuseRegisters,
5141cb0ef41Sopenharmony_ci                  used_regs);
5151cb0ef41Sopenharmony_ci  // Consistency check: All the {used_regs} are really in use now.
5161cb0ef41Sopenharmony_ci  DCHECK_EQ(used_regs, used_registers & used_regs);
5171cb0ef41Sopenharmony_ci
5181cb0ef41Sopenharmony_ci  // Last, initialize the section in between. Here, constants are allowed, but
5191cb0ef41Sopenharmony_ci  // registers which are already used for the merge region or locals must be
5201cb0ef41Sopenharmony_ci  // moved to other registers or spilled. If a register appears twice in the
5211cb0ef41Sopenharmony_ci  // source region, ensure to use the same register twice in the target region.
5221cb0ef41Sopenharmony_ci  InitMergeRegion(this, source_begin + num_locals, target_begin + num_locals,
5231cb0ef41Sopenharmony_ci                  stack_depth, kKeepStackSlots, kConstantsAllowed,
5241cb0ef41Sopenharmony_ci                  kRegistersAllowed, kReuseRegisters, used_regs);
5251cb0ef41Sopenharmony_ci}
5261cb0ef41Sopenharmony_ci
5271cb0ef41Sopenharmony_civoid LiftoffAssembler::CacheState::Steal(const CacheState& source) {
5281cb0ef41Sopenharmony_ci  // Just use the move assignment operator.
5291cb0ef41Sopenharmony_ci  *this = std::move(source);
5301cb0ef41Sopenharmony_ci}
5311cb0ef41Sopenharmony_ci
5321cb0ef41Sopenharmony_civoid LiftoffAssembler::CacheState::Split(const CacheState& source) {
5331cb0ef41Sopenharmony_ci  // Call the private copy assignment operator.
5341cb0ef41Sopenharmony_ci  *this = source;
5351cb0ef41Sopenharmony_ci}
5361cb0ef41Sopenharmony_ci
5371cb0ef41Sopenharmony_cinamespace {
5381cb0ef41Sopenharmony_ciint GetSafepointIndexForStackSlot(const VarState& slot) {
5391cb0ef41Sopenharmony_ci  // index = 0 is for the stack slot at 'fp + kFixedFrameSizeAboveFp -
5401cb0ef41Sopenharmony_ci  // kSystemPointerSize', the location of the current stack slot is 'fp -
5411cb0ef41Sopenharmony_ci  // slot.offset()'. The index we need is therefore '(fp +
5421cb0ef41Sopenharmony_ci  // kFixedFrameSizeAboveFp - kSystemPointerSize) - (fp - slot.offset())' =
5431cb0ef41Sopenharmony_ci  // 'slot.offset() + kFixedFrameSizeAboveFp - kSystemPointerSize'.
5441cb0ef41Sopenharmony_ci  // Concretely, the index of the first stack slot is '4'.
5451cb0ef41Sopenharmony_ci  return (slot.offset() + StandardFrameConstants::kFixedFrameSizeAboveFp -
5461cb0ef41Sopenharmony_ci          kSystemPointerSize) /
5471cb0ef41Sopenharmony_ci         kSystemPointerSize;
5481cb0ef41Sopenharmony_ci}
5491cb0ef41Sopenharmony_ci}  // namespace
5501cb0ef41Sopenharmony_ci
5511cb0ef41Sopenharmony_civoid LiftoffAssembler::CacheState::GetTaggedSlotsForOOLCode(
5521cb0ef41Sopenharmony_ci    ZoneVector<int>* slots, LiftoffRegList* spills,
5531cb0ef41Sopenharmony_ci    SpillLocation spill_location) {
5541cb0ef41Sopenharmony_ci  for (const auto& slot : stack_state) {
5551cb0ef41Sopenharmony_ci    if (!is_reference(slot.kind())) continue;
5561cb0ef41Sopenharmony_ci
5571cb0ef41Sopenharmony_ci    if (spill_location == SpillLocation::kTopOfStack && slot.is_reg()) {
5581cb0ef41Sopenharmony_ci      // Registers get spilled just before the call to the runtime. In {spills}
5591cb0ef41Sopenharmony_ci      // we store which of the spilled registers contain references, so that we
5601cb0ef41Sopenharmony_ci      // can add the spill slots to the safepoint.
5611cb0ef41Sopenharmony_ci      spills->set(slot.reg());
5621cb0ef41Sopenharmony_ci      continue;
5631cb0ef41Sopenharmony_ci    }
5641cb0ef41Sopenharmony_ci    DCHECK_IMPLIES(slot.is_reg(), spill_location == SpillLocation::kStackSlots);
5651cb0ef41Sopenharmony_ci
5661cb0ef41Sopenharmony_ci    slots->push_back(GetSafepointIndexForStackSlot(slot));
5671cb0ef41Sopenharmony_ci  }
5681cb0ef41Sopenharmony_ci}
5691cb0ef41Sopenharmony_ci
5701cb0ef41Sopenharmony_civoid LiftoffAssembler::CacheState::DefineSafepoint(
5711cb0ef41Sopenharmony_ci    SafepointTableBuilder::Safepoint& safepoint) {
5721cb0ef41Sopenharmony_ci  for (const auto& slot : stack_state) {
5731cb0ef41Sopenharmony_ci    if (is_reference(slot.kind())) {
5741cb0ef41Sopenharmony_ci      DCHECK(slot.is_stack());
5751cb0ef41Sopenharmony_ci      safepoint.DefineTaggedStackSlot(GetSafepointIndexForStackSlot(slot));
5761cb0ef41Sopenharmony_ci    }
5771cb0ef41Sopenharmony_ci  }
5781cb0ef41Sopenharmony_ci}
5791cb0ef41Sopenharmony_ci
5801cb0ef41Sopenharmony_civoid LiftoffAssembler::CacheState::DefineSafepointWithCalleeSavedRegisters(
5811cb0ef41Sopenharmony_ci    SafepointTableBuilder::Safepoint& safepoint) {
5821cb0ef41Sopenharmony_ci  for (const auto& slot : stack_state) {
5831cb0ef41Sopenharmony_ci    if (!is_reference(slot.kind())) continue;
5841cb0ef41Sopenharmony_ci    if (slot.is_stack()) {
5851cb0ef41Sopenharmony_ci      safepoint.DefineTaggedStackSlot(GetSafepointIndexForStackSlot(slot));
5861cb0ef41Sopenharmony_ci    } else {
5871cb0ef41Sopenharmony_ci      DCHECK(slot.is_reg());
5881cb0ef41Sopenharmony_ci      safepoint.DefineTaggedRegister(slot.reg().gp().code());
5891cb0ef41Sopenharmony_ci    }
5901cb0ef41Sopenharmony_ci  }
5911cb0ef41Sopenharmony_ci  if (cached_instance != no_reg) {
5921cb0ef41Sopenharmony_ci    safepoint.DefineTaggedRegister(cached_instance.code());
5931cb0ef41Sopenharmony_ci  }
5941cb0ef41Sopenharmony_ci}
5951cb0ef41Sopenharmony_ci
5961cb0ef41Sopenharmony_ciint LiftoffAssembler::GetTotalFrameSlotCountForGC() const {
5971cb0ef41Sopenharmony_ci  // The GC does not care about the actual number of spill slots, just about
5981cb0ef41Sopenharmony_ci  // the number of references that could be there in the spilling area. Note
5991cb0ef41Sopenharmony_ci  // that the offset of the first spill slot is kSystemPointerSize and not
6001cb0ef41Sopenharmony_ci  // '0'. Therefore we don't have to add '+1' here.
6011cb0ef41Sopenharmony_ci  return (max_used_spill_offset_ +
6021cb0ef41Sopenharmony_ci          StandardFrameConstants::kFixedFrameSizeAboveFp +
6031cb0ef41Sopenharmony_ci          ool_spill_space_size_) /
6041cb0ef41Sopenharmony_ci         kSystemPointerSize;
6051cb0ef41Sopenharmony_ci}
6061cb0ef41Sopenharmony_ci
6071cb0ef41Sopenharmony_cinamespace {
6081cb0ef41Sopenharmony_ci
6091cb0ef41Sopenharmony_ciAssemblerOptions DefaultLiftoffOptions() { return AssemblerOptions{}; }
6101cb0ef41Sopenharmony_ci
6111cb0ef41Sopenharmony_ci}  // namespace
6121cb0ef41Sopenharmony_ci
6131cb0ef41Sopenharmony_ciLiftoffAssembler::LiftoffAssembler(std::unique_ptr<AssemblerBuffer> buffer)
6141cb0ef41Sopenharmony_ci    : TurboAssembler(nullptr, DefaultLiftoffOptions(), CodeObjectRequired::kNo,
6151cb0ef41Sopenharmony_ci                     std::move(buffer)) {
6161cb0ef41Sopenharmony_ci  set_abort_hard(true);  // Avoid calls to Abort.
6171cb0ef41Sopenharmony_ci}
6181cb0ef41Sopenharmony_ci
6191cb0ef41Sopenharmony_ciLiftoffAssembler::~LiftoffAssembler() {
6201cb0ef41Sopenharmony_ci  if (num_locals_ > kInlineLocalKinds) {
6211cb0ef41Sopenharmony_ci    base::Free(more_local_kinds_);
6221cb0ef41Sopenharmony_ci  }
6231cb0ef41Sopenharmony_ci}
6241cb0ef41Sopenharmony_ci
6251cb0ef41Sopenharmony_ciLiftoffRegister LiftoffAssembler::LoadToRegister(VarState slot,
6261cb0ef41Sopenharmony_ci                                                 LiftoffRegList pinned) {
6271cb0ef41Sopenharmony_ci  if (slot.is_reg()) return slot.reg();
6281cb0ef41Sopenharmony_ci  LiftoffRegister reg = GetUnusedRegister(reg_class_for(slot.kind()), pinned);
6291cb0ef41Sopenharmony_ci  if (slot.is_const()) {
6301cb0ef41Sopenharmony_ci    LoadConstant(reg, slot.constant());
6311cb0ef41Sopenharmony_ci  } else {
6321cb0ef41Sopenharmony_ci    DCHECK(slot.is_stack());
6331cb0ef41Sopenharmony_ci    Fill(reg, slot.offset(), slot.kind());
6341cb0ef41Sopenharmony_ci  }
6351cb0ef41Sopenharmony_ci  return reg;
6361cb0ef41Sopenharmony_ci}
6371cb0ef41Sopenharmony_ci
6381cb0ef41Sopenharmony_ciLiftoffRegister LiftoffAssembler::LoadI64HalfIntoRegister(VarState slot,
6391cb0ef41Sopenharmony_ci                                                          RegPairHalf half) {
6401cb0ef41Sopenharmony_ci  if (slot.is_reg()) {
6411cb0ef41Sopenharmony_ci    return half == kLowWord ? slot.reg().low() : slot.reg().high();
6421cb0ef41Sopenharmony_ci  }
6431cb0ef41Sopenharmony_ci  LiftoffRegister dst = GetUnusedRegister(kGpReg, {});
6441cb0ef41Sopenharmony_ci  if (slot.is_stack()) {
6451cb0ef41Sopenharmony_ci    FillI64Half(dst.gp(), slot.offset(), half);
6461cb0ef41Sopenharmony_ci    return dst;
6471cb0ef41Sopenharmony_ci  }
6481cb0ef41Sopenharmony_ci  DCHECK(slot.is_const());
6491cb0ef41Sopenharmony_ci  int32_t half_word =
6501cb0ef41Sopenharmony_ci      static_cast<int32_t>(half == kLowWord ? slot.constant().to_i64()
6511cb0ef41Sopenharmony_ci                                            : slot.constant().to_i64() >> 32);
6521cb0ef41Sopenharmony_ci  LoadConstant(dst, WasmValue(half_word));
6531cb0ef41Sopenharmony_ci  return dst;
6541cb0ef41Sopenharmony_ci}
6551cb0ef41Sopenharmony_ci
6561cb0ef41Sopenharmony_ciLiftoffRegister LiftoffAssembler::PeekToRegister(int index,
6571cb0ef41Sopenharmony_ci                                                 LiftoffRegList pinned) {
6581cb0ef41Sopenharmony_ci  DCHECK_LT(index, cache_state_.stack_state.size());
6591cb0ef41Sopenharmony_ci  VarState& slot = cache_state_.stack_state.end()[-1 - index];
6601cb0ef41Sopenharmony_ci  if (slot.is_reg()) {
6611cb0ef41Sopenharmony_ci    return slot.reg();
6621cb0ef41Sopenharmony_ci  }
6631cb0ef41Sopenharmony_ci  LiftoffRegister reg = LoadToRegister(slot, pinned);
6641cb0ef41Sopenharmony_ci  cache_state_.inc_used(reg);
6651cb0ef41Sopenharmony_ci  slot.MakeRegister(reg);
6661cb0ef41Sopenharmony_ci  return reg;
6671cb0ef41Sopenharmony_ci}
6681cb0ef41Sopenharmony_ci
6691cb0ef41Sopenharmony_civoid LiftoffAssembler::DropValues(int count) {
6701cb0ef41Sopenharmony_ci  for (int i = 0; i < count; ++i) {
6711cb0ef41Sopenharmony_ci    DCHECK(!cache_state_.stack_state.empty());
6721cb0ef41Sopenharmony_ci    VarState slot = cache_state_.stack_state.back();
6731cb0ef41Sopenharmony_ci    cache_state_.stack_state.pop_back();
6741cb0ef41Sopenharmony_ci    if (slot.is_reg()) {
6751cb0ef41Sopenharmony_ci      cache_state_.dec_used(slot.reg());
6761cb0ef41Sopenharmony_ci    }
6771cb0ef41Sopenharmony_ci  }
6781cb0ef41Sopenharmony_ci}
6791cb0ef41Sopenharmony_ci
6801cb0ef41Sopenharmony_civoid LiftoffAssembler::DropValue(int depth) {
6811cb0ef41Sopenharmony_ci  auto* dropped = cache_state_.stack_state.begin() + depth;
6821cb0ef41Sopenharmony_ci  if (dropped->is_reg()) {
6831cb0ef41Sopenharmony_ci    cache_state_.dec_used(dropped->reg());
6841cb0ef41Sopenharmony_ci  }
6851cb0ef41Sopenharmony_ci  std::copy(dropped + 1, cache_state_.stack_state.end(), dropped);
6861cb0ef41Sopenharmony_ci  cache_state_.stack_state.pop_back();
6871cb0ef41Sopenharmony_ci}
6881cb0ef41Sopenharmony_ci
6891cb0ef41Sopenharmony_civoid LiftoffAssembler::PrepareLoopArgs(int num) {
6901cb0ef41Sopenharmony_ci  for (int i = 0; i < num; ++i) {
6911cb0ef41Sopenharmony_ci    VarState& slot = cache_state_.stack_state.end()[-1 - i];
6921cb0ef41Sopenharmony_ci    if (slot.is_stack()) continue;
6931cb0ef41Sopenharmony_ci    RegClass rc = reg_class_for(slot.kind());
6941cb0ef41Sopenharmony_ci    if (slot.is_reg()) {
6951cb0ef41Sopenharmony_ci      if (cache_state_.get_use_count(slot.reg()) > 1) {
6961cb0ef41Sopenharmony_ci        // If the register is used more than once, we cannot use it for the
6971cb0ef41Sopenharmony_ci        // merge. Move it to an unused register instead.
6981cb0ef41Sopenharmony_ci        LiftoffRegList pinned;
6991cb0ef41Sopenharmony_ci        pinned.set(slot.reg());
7001cb0ef41Sopenharmony_ci        LiftoffRegister dst_reg = GetUnusedRegister(rc, pinned);
7011cb0ef41Sopenharmony_ci        Move(dst_reg, slot.reg(), slot.kind());
7021cb0ef41Sopenharmony_ci        cache_state_.dec_used(slot.reg());
7031cb0ef41Sopenharmony_ci        cache_state_.inc_used(dst_reg);
7041cb0ef41Sopenharmony_ci        slot.MakeRegister(dst_reg);
7051cb0ef41Sopenharmony_ci      }
7061cb0ef41Sopenharmony_ci      continue;
7071cb0ef41Sopenharmony_ci    }
7081cb0ef41Sopenharmony_ci    LiftoffRegister reg = GetUnusedRegister(rc, {});
7091cb0ef41Sopenharmony_ci    LoadConstant(reg, slot.constant());
7101cb0ef41Sopenharmony_ci    slot.MakeRegister(reg);
7111cb0ef41Sopenharmony_ci    cache_state_.inc_used(reg);
7121cb0ef41Sopenharmony_ci  }
7131cb0ef41Sopenharmony_ci}
7141cb0ef41Sopenharmony_ci
7151cb0ef41Sopenharmony_civoid LiftoffAssembler::MaterializeMergedConstants(uint32_t arity) {
7161cb0ef41Sopenharmony_ci  // Materialize constants on top of the stack ({arity} many), and locals.
7171cb0ef41Sopenharmony_ci  VarState* stack_base = cache_state_.stack_state.data();
7181cb0ef41Sopenharmony_ci  for (auto slots :
7191cb0ef41Sopenharmony_ci       {base::VectorOf(stack_base + cache_state_.stack_state.size() - arity,
7201cb0ef41Sopenharmony_ci                       arity),
7211cb0ef41Sopenharmony_ci        base::VectorOf(stack_base, num_locals())}) {
7221cb0ef41Sopenharmony_ci    for (VarState& slot : slots) {
7231cb0ef41Sopenharmony_ci      if (!slot.is_const()) continue;
7241cb0ef41Sopenharmony_ci      RegClass rc = reg_class_for(slot.kind());
7251cb0ef41Sopenharmony_ci      if (cache_state_.has_unused_register(rc)) {
7261cb0ef41Sopenharmony_ci        LiftoffRegister reg = cache_state_.unused_register(rc);
7271cb0ef41Sopenharmony_ci        LoadConstant(reg, slot.constant());
7281cb0ef41Sopenharmony_ci        cache_state_.inc_used(reg);
7291cb0ef41Sopenharmony_ci        slot.MakeRegister(reg);
7301cb0ef41Sopenharmony_ci      } else {
7311cb0ef41Sopenharmony_ci        Spill(slot.offset(), slot.constant());
7321cb0ef41Sopenharmony_ci        slot.MakeStack();
7331cb0ef41Sopenharmony_ci      }
7341cb0ef41Sopenharmony_ci    }
7351cb0ef41Sopenharmony_ci  }
7361cb0ef41Sopenharmony_ci}
7371cb0ef41Sopenharmony_ci
7381cb0ef41Sopenharmony_ci#ifdef DEBUG
7391cb0ef41Sopenharmony_cinamespace {
7401cb0ef41Sopenharmony_cibool SlotInterference(const VarState& a, const VarState& b) {
7411cb0ef41Sopenharmony_ci  return a.is_stack() && b.is_stack() &&
7421cb0ef41Sopenharmony_ci         b.offset() > a.offset() - value_kind_size(a.kind()) &&
7431cb0ef41Sopenharmony_ci         b.offset() - value_kind_size(b.kind()) < a.offset();
7441cb0ef41Sopenharmony_ci}
7451cb0ef41Sopenharmony_ci
7461cb0ef41Sopenharmony_cibool SlotInterference(const VarState& a, base::Vector<const VarState> v) {
7471cb0ef41Sopenharmony_ci  return std::any_of(v.begin(), v.end(), [&a](const VarState& b) {
7481cb0ef41Sopenharmony_ci    return SlotInterference(a, b);
7491cb0ef41Sopenharmony_ci  });
7501cb0ef41Sopenharmony_ci}
7511cb0ef41Sopenharmony_ci}  // namespace
7521cb0ef41Sopenharmony_ci#endif
7531cb0ef41Sopenharmony_ci
7541cb0ef41Sopenharmony_civoid LiftoffAssembler::MergeFullStackWith(CacheState& target,
7551cb0ef41Sopenharmony_ci                                          const CacheState& source) {
7561cb0ef41Sopenharmony_ci  DCHECK_EQ(source.stack_height(), target.stack_height());
7571cb0ef41Sopenharmony_ci  // TODO(clemensb): Reuse the same StackTransferRecipe object to save some
7581cb0ef41Sopenharmony_ci  // allocations.
7591cb0ef41Sopenharmony_ci  StackTransferRecipe transfers(this);
7601cb0ef41Sopenharmony_ci  for (uint32_t i = 0, e = source.stack_height(); i < e; ++i) {
7611cb0ef41Sopenharmony_ci    transfers.TransferStackSlot(target.stack_state[i], source.stack_state[i]);
7621cb0ef41Sopenharmony_ci    DCHECK(!SlotInterference(target.stack_state[i],
7631cb0ef41Sopenharmony_ci                             base::VectorOf(source.stack_state.data() + i + 1,
7641cb0ef41Sopenharmony_ci                                            source.stack_height() - i - 1)));
7651cb0ef41Sopenharmony_ci  }
7661cb0ef41Sopenharmony_ci
7671cb0ef41Sopenharmony_ci  // Full stack merging is only done for forward jumps, so we can just clear the
7681cb0ef41Sopenharmony_ci  // cache registers at the target in case of mismatch.
7691cb0ef41Sopenharmony_ci  if (source.cached_instance != target.cached_instance) {
7701cb0ef41Sopenharmony_ci    target.ClearCachedInstanceRegister();
7711cb0ef41Sopenharmony_ci  }
7721cb0ef41Sopenharmony_ci  if (source.cached_mem_start != target.cached_mem_start) {
7731cb0ef41Sopenharmony_ci    target.ClearCachedMemStartRegister();
7741cb0ef41Sopenharmony_ci  }
7751cb0ef41Sopenharmony_ci}
7761cb0ef41Sopenharmony_ci
7771cb0ef41Sopenharmony_civoid LiftoffAssembler::MergeStackWith(CacheState& target, uint32_t arity,
7781cb0ef41Sopenharmony_ci                                      JumpDirection jump_direction) {
7791cb0ef41Sopenharmony_ci  // Before: ----------------|----- (discarded) ----|--- arity ---|
7801cb0ef41Sopenharmony_ci  //                         ^target_stack_height   ^stack_base   ^stack_height
7811cb0ef41Sopenharmony_ci  // After:  ----|-- arity --|
7821cb0ef41Sopenharmony_ci  //             ^           ^target_stack_height
7831cb0ef41Sopenharmony_ci  //             ^target_stack_base
7841cb0ef41Sopenharmony_ci  uint32_t stack_height = cache_state_.stack_height();
7851cb0ef41Sopenharmony_ci  uint32_t target_stack_height = target.stack_height();
7861cb0ef41Sopenharmony_ci  DCHECK_LE(target_stack_height, stack_height);
7871cb0ef41Sopenharmony_ci  DCHECK_LE(arity, target_stack_height);
7881cb0ef41Sopenharmony_ci  uint32_t stack_base = stack_height - arity;
7891cb0ef41Sopenharmony_ci  uint32_t target_stack_base = target_stack_height - arity;
7901cb0ef41Sopenharmony_ci  StackTransferRecipe transfers(this);
7911cb0ef41Sopenharmony_ci  for (uint32_t i = 0; i < target_stack_base; ++i) {
7921cb0ef41Sopenharmony_ci    transfers.TransferStackSlot(target.stack_state[i],
7931cb0ef41Sopenharmony_ci                                cache_state_.stack_state[i]);
7941cb0ef41Sopenharmony_ci    DCHECK(!SlotInterference(
7951cb0ef41Sopenharmony_ci        target.stack_state[i],
7961cb0ef41Sopenharmony_ci        base::VectorOf(cache_state_.stack_state.data() + i + 1,
7971cb0ef41Sopenharmony_ci                       target_stack_base - i - 1)));
7981cb0ef41Sopenharmony_ci    DCHECK(!SlotInterference(
7991cb0ef41Sopenharmony_ci        target.stack_state[i],
8001cb0ef41Sopenharmony_ci        base::VectorOf(cache_state_.stack_state.data() + stack_base, arity)));
8011cb0ef41Sopenharmony_ci  }
8021cb0ef41Sopenharmony_ci  for (uint32_t i = 0; i < arity; ++i) {
8031cb0ef41Sopenharmony_ci    transfers.TransferStackSlot(target.stack_state[target_stack_base + i],
8041cb0ef41Sopenharmony_ci                                cache_state_.stack_state[stack_base + i]);
8051cb0ef41Sopenharmony_ci    DCHECK(!SlotInterference(
8061cb0ef41Sopenharmony_ci        target.stack_state[target_stack_base + i],
8071cb0ef41Sopenharmony_ci        base::VectorOf(cache_state_.stack_state.data() + stack_base + i + 1,
8081cb0ef41Sopenharmony_ci                       arity - i - 1)));
8091cb0ef41Sopenharmony_ci  }
8101cb0ef41Sopenharmony_ci
8111cb0ef41Sopenharmony_ci  // Check whether the cached instance and/or memory start need to be moved to
8121cb0ef41Sopenharmony_ci  // another register. Register moves are executed as part of the
8131cb0ef41Sopenharmony_ci  // {StackTransferRecipe}. Remember whether the register content has to be
8141cb0ef41Sopenharmony_ci  // reloaded after executing the stack transfers.
8151cb0ef41Sopenharmony_ci  bool reload_instance = false;
8161cb0ef41Sopenharmony_ci  bool reload_mem_start = false;
8171cb0ef41Sopenharmony_ci  for (auto tuple :
8181cb0ef41Sopenharmony_ci       {std::make_tuple(&reload_instance, cache_state_.cached_instance,
8191cb0ef41Sopenharmony_ci                        &target.cached_instance),
8201cb0ef41Sopenharmony_ci        std::make_tuple(&reload_mem_start, cache_state_.cached_mem_start,
8211cb0ef41Sopenharmony_ci                        &target.cached_mem_start)}) {
8221cb0ef41Sopenharmony_ci    bool* reload = std::get<0>(tuple);
8231cb0ef41Sopenharmony_ci    Register src_reg = std::get<1>(tuple);
8241cb0ef41Sopenharmony_ci    Register* dst_reg = std::get<2>(tuple);
8251cb0ef41Sopenharmony_ci    // If the registers match, or the destination has no cache register, nothing
8261cb0ef41Sopenharmony_ci    // needs to be done.
8271cb0ef41Sopenharmony_ci    if (src_reg == *dst_reg || *dst_reg == no_reg) continue;
8281cb0ef41Sopenharmony_ci    // On forward jumps, just reset the cached register in the target state.
8291cb0ef41Sopenharmony_ci    if (jump_direction == kForwardJump) {
8301cb0ef41Sopenharmony_ci      target.ClearCacheRegister(dst_reg);
8311cb0ef41Sopenharmony_ci    } else if (src_reg != no_reg) {
8321cb0ef41Sopenharmony_ci      // If the source has the content but in the wrong register, execute a
8331cb0ef41Sopenharmony_ci      // register move as part of the stack transfer.
8341cb0ef41Sopenharmony_ci      transfers.MoveRegister(LiftoffRegister{*dst_reg},
8351cb0ef41Sopenharmony_ci                             LiftoffRegister{src_reg}, kPointerKind);
8361cb0ef41Sopenharmony_ci    } else {
8371cb0ef41Sopenharmony_ci      // Otherwise (the source state has no cached content), we reload later.
8381cb0ef41Sopenharmony_ci      *reload = true;
8391cb0ef41Sopenharmony_ci    }
8401cb0ef41Sopenharmony_ci  }
8411cb0ef41Sopenharmony_ci
8421cb0ef41Sopenharmony_ci  // Now execute stack transfers and register moves/loads.
8431cb0ef41Sopenharmony_ci  transfers.Execute();
8441cb0ef41Sopenharmony_ci
8451cb0ef41Sopenharmony_ci  if (reload_instance) {
8461cb0ef41Sopenharmony_ci    LoadInstanceFromFrame(target.cached_instance);
8471cb0ef41Sopenharmony_ci  }
8481cb0ef41Sopenharmony_ci  if (reload_mem_start) {
8491cb0ef41Sopenharmony_ci    // {target.cached_instance} already got restored above, so we can use it
8501cb0ef41Sopenharmony_ci    // if it exists.
8511cb0ef41Sopenharmony_ci    Register instance = target.cached_instance;
8521cb0ef41Sopenharmony_ci    if (instance == no_reg) {
8531cb0ef41Sopenharmony_ci      // We don't have the instance available yet. Store it into the target
8541cb0ef41Sopenharmony_ci      // mem_start, so that we can load the mem_start from there.
8551cb0ef41Sopenharmony_ci      instance = target.cached_mem_start;
8561cb0ef41Sopenharmony_ci      LoadInstanceFromFrame(instance);
8571cb0ef41Sopenharmony_ci    }
8581cb0ef41Sopenharmony_ci    LoadFromInstance(
8591cb0ef41Sopenharmony_ci        target.cached_mem_start, instance,
8601cb0ef41Sopenharmony_ci        ObjectAccess::ToTagged(WasmInstanceObject::kMemoryStartOffset),
8611cb0ef41Sopenharmony_ci        sizeof(size_t));
8621cb0ef41Sopenharmony_ci#ifdef V8_SANDBOXED_POINTERS
8631cb0ef41Sopenharmony_ci    DecodeSandboxedPointer(target.cached_mem_start);
8641cb0ef41Sopenharmony_ci#endif
8651cb0ef41Sopenharmony_ci  }
8661cb0ef41Sopenharmony_ci}
8671cb0ef41Sopenharmony_ci
8681cb0ef41Sopenharmony_civoid LiftoffAssembler::Spill(VarState* slot) {
8691cb0ef41Sopenharmony_ci  switch (slot->loc()) {
8701cb0ef41Sopenharmony_ci    case VarState::kStack:
8711cb0ef41Sopenharmony_ci      return;
8721cb0ef41Sopenharmony_ci    case VarState::kRegister:
8731cb0ef41Sopenharmony_ci      Spill(slot->offset(), slot->reg(), slot->kind());
8741cb0ef41Sopenharmony_ci      cache_state_.dec_used(slot->reg());
8751cb0ef41Sopenharmony_ci      break;
8761cb0ef41Sopenharmony_ci    case VarState::kIntConst:
8771cb0ef41Sopenharmony_ci      Spill(slot->offset(), slot->constant());
8781cb0ef41Sopenharmony_ci      break;
8791cb0ef41Sopenharmony_ci  }
8801cb0ef41Sopenharmony_ci  slot->MakeStack();
8811cb0ef41Sopenharmony_ci}
8821cb0ef41Sopenharmony_ci
8831cb0ef41Sopenharmony_civoid LiftoffAssembler::SpillLocals() {
8841cb0ef41Sopenharmony_ci  for (uint32_t i = 0; i < num_locals_; ++i) {
8851cb0ef41Sopenharmony_ci    Spill(&cache_state_.stack_state[i]);
8861cb0ef41Sopenharmony_ci  }
8871cb0ef41Sopenharmony_ci}
8881cb0ef41Sopenharmony_ci
8891cb0ef41Sopenharmony_civoid LiftoffAssembler::SpillAllRegisters() {
8901cb0ef41Sopenharmony_ci  for (uint32_t i = 0, e = cache_state_.stack_height(); i < e; ++i) {
8911cb0ef41Sopenharmony_ci    auto& slot = cache_state_.stack_state[i];
8921cb0ef41Sopenharmony_ci    if (!slot.is_reg()) continue;
8931cb0ef41Sopenharmony_ci    Spill(slot.offset(), slot.reg(), slot.kind());
8941cb0ef41Sopenharmony_ci    slot.MakeStack();
8951cb0ef41Sopenharmony_ci  }
8961cb0ef41Sopenharmony_ci  cache_state_.ClearAllCacheRegisters();
8971cb0ef41Sopenharmony_ci  cache_state_.reset_used_registers();
8981cb0ef41Sopenharmony_ci}
8991cb0ef41Sopenharmony_ci
9001cb0ef41Sopenharmony_civoid LiftoffAssembler::ClearRegister(
9011cb0ef41Sopenharmony_ci    Register reg, std::initializer_list<Register*> possible_uses,
9021cb0ef41Sopenharmony_ci    LiftoffRegList pinned) {
9031cb0ef41Sopenharmony_ci  if (reg == cache_state()->cached_instance) {
9041cb0ef41Sopenharmony_ci    cache_state()->ClearCachedInstanceRegister();
9051cb0ef41Sopenharmony_ci    // We can return immediately. The instance is only used to load information
9061cb0ef41Sopenharmony_ci    // at the beginning of an instruction when values don't have to be in
9071cb0ef41Sopenharmony_ci    // specific registers yet. Therefore the instance should never be one of the
9081cb0ef41Sopenharmony_ci    // {possible_uses}.
9091cb0ef41Sopenharmony_ci    for (Register* use : possible_uses) {
9101cb0ef41Sopenharmony_ci      USE(use);
9111cb0ef41Sopenharmony_ci      DCHECK_NE(reg, *use);
9121cb0ef41Sopenharmony_ci    }
9131cb0ef41Sopenharmony_ci    return;
9141cb0ef41Sopenharmony_ci  } else if (reg == cache_state()->cached_mem_start) {
9151cb0ef41Sopenharmony_ci    cache_state()->ClearCachedMemStartRegister();
9161cb0ef41Sopenharmony_ci    // The memory start may be among the {possible_uses}, e.g. for an atomic
9171cb0ef41Sopenharmony_ci    // compare exchange. Therefore it is necessary to iterate over the
9181cb0ef41Sopenharmony_ci    // {possible_uses} below, and we cannot return early.
9191cb0ef41Sopenharmony_ci  } else if (cache_state()->is_used(LiftoffRegister(reg))) {
9201cb0ef41Sopenharmony_ci    SpillRegister(LiftoffRegister(reg));
9211cb0ef41Sopenharmony_ci  }
9221cb0ef41Sopenharmony_ci  Register replacement = no_reg;
9231cb0ef41Sopenharmony_ci  for (Register* use : possible_uses) {
9241cb0ef41Sopenharmony_ci    if (reg != *use) continue;
9251cb0ef41Sopenharmony_ci    if (replacement == no_reg) {
9261cb0ef41Sopenharmony_ci      replacement = GetUnusedRegister(kGpReg, pinned).gp();
9271cb0ef41Sopenharmony_ci      Move(replacement, reg, kPointerKind);
9281cb0ef41Sopenharmony_ci    }
9291cb0ef41Sopenharmony_ci    // We cannot leave this loop early. There may be multiple uses of {reg}.
9301cb0ef41Sopenharmony_ci    *use = replacement;
9311cb0ef41Sopenharmony_ci  }
9321cb0ef41Sopenharmony_ci}
9331cb0ef41Sopenharmony_ci
9341cb0ef41Sopenharmony_cinamespace {
9351cb0ef41Sopenharmony_civoid PrepareStackTransfers(const ValueKindSig* sig,
9361cb0ef41Sopenharmony_ci                           compiler::CallDescriptor* call_descriptor,
9371cb0ef41Sopenharmony_ci                           const VarState* slots,
9381cb0ef41Sopenharmony_ci                           LiftoffStackSlots* stack_slots,
9391cb0ef41Sopenharmony_ci                           StackTransferRecipe* stack_transfers,
9401cb0ef41Sopenharmony_ci                           LiftoffRegList* param_regs) {
9411cb0ef41Sopenharmony_ci  // Process parameters backwards, to reduce the amount of Slot sorting for
9421cb0ef41Sopenharmony_ci  // the most common case - a normal Wasm Call. Slots will be mostly unsorted
9431cb0ef41Sopenharmony_ci  // in the Builtin call case.
9441cb0ef41Sopenharmony_ci  uint32_t call_desc_input_idx =
9451cb0ef41Sopenharmony_ci      static_cast<uint32_t>(call_descriptor->InputCount());
9461cb0ef41Sopenharmony_ci  uint32_t num_params = static_cast<uint32_t>(sig->parameter_count());
9471cb0ef41Sopenharmony_ci  for (uint32_t i = num_params; i > 0; --i) {
9481cb0ef41Sopenharmony_ci    const uint32_t param = i - 1;
9491cb0ef41Sopenharmony_ci    ValueKind kind = sig->GetParam(param);
9501cb0ef41Sopenharmony_ci    const bool is_gp_pair = kNeedI64RegPair && kind == kI64;
9511cb0ef41Sopenharmony_ci    const int num_lowered_params = is_gp_pair ? 2 : 1;
9521cb0ef41Sopenharmony_ci    const VarState& slot = slots[param];
9531cb0ef41Sopenharmony_ci    const uint32_t stack_offset = slot.offset();
9541cb0ef41Sopenharmony_ci    // Process both halfs of a register pair separately, because they are passed
9551cb0ef41Sopenharmony_ci    // as separate parameters. One or both of them could end up on the stack.
9561cb0ef41Sopenharmony_ci    for (int lowered_idx = 0; lowered_idx < num_lowered_params; ++lowered_idx) {
9571cb0ef41Sopenharmony_ci      const RegPairHalf half =
9581cb0ef41Sopenharmony_ci          is_gp_pair && lowered_idx == 0 ? kHighWord : kLowWord;
9591cb0ef41Sopenharmony_ci      --call_desc_input_idx;
9601cb0ef41Sopenharmony_ci      compiler::LinkageLocation loc =
9611cb0ef41Sopenharmony_ci          call_descriptor->GetInputLocation(call_desc_input_idx);
9621cb0ef41Sopenharmony_ci      if (loc.IsRegister()) {
9631cb0ef41Sopenharmony_ci        DCHECK(!loc.IsAnyRegister());
9641cb0ef41Sopenharmony_ci        RegClass rc = is_gp_pair ? kGpReg : reg_class_for(kind);
9651cb0ef41Sopenharmony_ci        int reg_code = loc.AsRegister();
9661cb0ef41Sopenharmony_ci        LiftoffRegister reg =
9671cb0ef41Sopenharmony_ci            LiftoffRegister::from_external_code(rc, kind, reg_code);
9681cb0ef41Sopenharmony_ci        param_regs->set(reg);
9691cb0ef41Sopenharmony_ci        if (is_gp_pair) {
9701cb0ef41Sopenharmony_ci          stack_transfers->LoadI64HalfIntoRegister(reg, slot, stack_offset,
9711cb0ef41Sopenharmony_ci                                                   half);
9721cb0ef41Sopenharmony_ci        } else {
9731cb0ef41Sopenharmony_ci          stack_transfers->LoadIntoRegister(reg, slot, stack_offset);
9741cb0ef41Sopenharmony_ci        }
9751cb0ef41Sopenharmony_ci      } else {
9761cb0ef41Sopenharmony_ci        DCHECK(loc.IsCallerFrameSlot());
9771cb0ef41Sopenharmony_ci        int param_offset = -loc.GetLocation() - 1;
9781cb0ef41Sopenharmony_ci        stack_slots->Add(slot, stack_offset, half, param_offset);
9791cb0ef41Sopenharmony_ci      }
9801cb0ef41Sopenharmony_ci    }
9811cb0ef41Sopenharmony_ci  }
9821cb0ef41Sopenharmony_ci}
9831cb0ef41Sopenharmony_ci
9841cb0ef41Sopenharmony_ci}  // namespace
9851cb0ef41Sopenharmony_ci
9861cb0ef41Sopenharmony_civoid LiftoffAssembler::PrepareBuiltinCall(
9871cb0ef41Sopenharmony_ci    const ValueKindSig* sig, compiler::CallDescriptor* call_descriptor,
9881cb0ef41Sopenharmony_ci    std::initializer_list<VarState> params) {
9891cb0ef41Sopenharmony_ci  LiftoffStackSlots stack_slots(this);
9901cb0ef41Sopenharmony_ci  StackTransferRecipe stack_transfers(this);
9911cb0ef41Sopenharmony_ci  LiftoffRegList param_regs;
9921cb0ef41Sopenharmony_ci  PrepareStackTransfers(sig, call_descriptor, params.begin(), &stack_slots,
9931cb0ef41Sopenharmony_ci                        &stack_transfers, &param_regs);
9941cb0ef41Sopenharmony_ci  SpillAllRegisters();
9951cb0ef41Sopenharmony_ci  int param_slots = static_cast<int>(call_descriptor->ParameterSlotCount());
9961cb0ef41Sopenharmony_ci  if (param_slots > 0) {
9971cb0ef41Sopenharmony_ci    stack_slots.Construct(param_slots);
9981cb0ef41Sopenharmony_ci  }
9991cb0ef41Sopenharmony_ci  // Execute the stack transfers before filling the instance register.
10001cb0ef41Sopenharmony_ci  stack_transfers.Execute();
10011cb0ef41Sopenharmony_ci
10021cb0ef41Sopenharmony_ci  // Reset register use counters.
10031cb0ef41Sopenharmony_ci  cache_state_.reset_used_registers();
10041cb0ef41Sopenharmony_ci}
10051cb0ef41Sopenharmony_ci
10061cb0ef41Sopenharmony_civoid LiftoffAssembler::PrepareCall(const ValueKindSig* sig,
10071cb0ef41Sopenharmony_ci                                   compiler::CallDescriptor* call_descriptor,
10081cb0ef41Sopenharmony_ci                                   Register* target,
10091cb0ef41Sopenharmony_ci                                   Register* target_instance) {
10101cb0ef41Sopenharmony_ci  uint32_t num_params = static_cast<uint32_t>(sig->parameter_count());
10111cb0ef41Sopenharmony_ci  // Input 0 is the call target.
10121cb0ef41Sopenharmony_ci  constexpr size_t kInputShift = 1;
10131cb0ef41Sopenharmony_ci
10141cb0ef41Sopenharmony_ci  // Spill all cache slots which are not being used as parameters.
10151cb0ef41Sopenharmony_ci  cache_state_.ClearAllCacheRegisters();
10161cb0ef41Sopenharmony_ci  for (VarState* it = cache_state_.stack_state.end() - 1 - num_params;
10171cb0ef41Sopenharmony_ci       it >= cache_state_.stack_state.begin() &&
10181cb0ef41Sopenharmony_ci       !cache_state_.used_registers.is_empty();
10191cb0ef41Sopenharmony_ci       --it) {
10201cb0ef41Sopenharmony_ci    if (!it->is_reg()) continue;
10211cb0ef41Sopenharmony_ci    Spill(it->offset(), it->reg(), it->kind());
10221cb0ef41Sopenharmony_ci    cache_state_.dec_used(it->reg());
10231cb0ef41Sopenharmony_ci    it->MakeStack();
10241cb0ef41Sopenharmony_ci  }
10251cb0ef41Sopenharmony_ci
10261cb0ef41Sopenharmony_ci  LiftoffStackSlots stack_slots(this);
10271cb0ef41Sopenharmony_ci  StackTransferRecipe stack_transfers(this);
10281cb0ef41Sopenharmony_ci  LiftoffRegList param_regs;
10291cb0ef41Sopenharmony_ci
10301cb0ef41Sopenharmony_ci  // Move the target instance (if supplied) into the correct instance register.
10311cb0ef41Sopenharmony_ci  compiler::LinkageLocation instance_loc =
10321cb0ef41Sopenharmony_ci      call_descriptor->GetInputLocation(kInputShift);
10331cb0ef41Sopenharmony_ci  DCHECK(instance_loc.IsRegister() && !instance_loc.IsAnyRegister());
10341cb0ef41Sopenharmony_ci  Register instance_reg = Register::from_code(instance_loc.AsRegister());
10351cb0ef41Sopenharmony_ci  param_regs.set(instance_reg);
10361cb0ef41Sopenharmony_ci  if (target_instance && *target_instance != instance_reg) {
10371cb0ef41Sopenharmony_ci    stack_transfers.MoveRegister(LiftoffRegister(instance_reg),
10381cb0ef41Sopenharmony_ci                                 LiftoffRegister(*target_instance),
10391cb0ef41Sopenharmony_ci                                 kPointerKind);
10401cb0ef41Sopenharmony_ci  }
10411cb0ef41Sopenharmony_ci
10421cb0ef41Sopenharmony_ci  int param_slots = static_cast<int>(call_descriptor->ParameterSlotCount());
10431cb0ef41Sopenharmony_ci  if (num_params) {
10441cb0ef41Sopenharmony_ci    uint32_t param_base = cache_state_.stack_height() - num_params;
10451cb0ef41Sopenharmony_ci    PrepareStackTransfers(sig, call_descriptor,
10461cb0ef41Sopenharmony_ci                          &cache_state_.stack_state[param_base], &stack_slots,
10471cb0ef41Sopenharmony_ci                          &stack_transfers, &param_regs);
10481cb0ef41Sopenharmony_ci  }
10491cb0ef41Sopenharmony_ci
10501cb0ef41Sopenharmony_ci  // If the target register overlaps with a parameter register, then move the
10511cb0ef41Sopenharmony_ci  // target to another free register, or spill to the stack.
10521cb0ef41Sopenharmony_ci  if (target && param_regs.has(LiftoffRegister(*target))) {
10531cb0ef41Sopenharmony_ci    // Try to find another free register.
10541cb0ef41Sopenharmony_ci    LiftoffRegList free_regs = kGpCacheRegList.MaskOut(param_regs);
10551cb0ef41Sopenharmony_ci    if (!free_regs.is_empty()) {
10561cb0ef41Sopenharmony_ci      LiftoffRegister new_target = free_regs.GetFirstRegSet();
10571cb0ef41Sopenharmony_ci      stack_transfers.MoveRegister(new_target, LiftoffRegister(*target),
10581cb0ef41Sopenharmony_ci                                   kPointerKind);
10591cb0ef41Sopenharmony_ci      *target = new_target.gp();
10601cb0ef41Sopenharmony_ci    } else {
10611cb0ef41Sopenharmony_ci      stack_slots.Add(VarState(kPointerKind, LiftoffRegister(*target), 0),
10621cb0ef41Sopenharmony_ci                      param_slots);
10631cb0ef41Sopenharmony_ci      param_slots++;
10641cb0ef41Sopenharmony_ci      *target = no_reg;
10651cb0ef41Sopenharmony_ci    }
10661cb0ef41Sopenharmony_ci  }
10671cb0ef41Sopenharmony_ci
10681cb0ef41Sopenharmony_ci  if (param_slots > 0) {
10691cb0ef41Sopenharmony_ci    stack_slots.Construct(param_slots);
10701cb0ef41Sopenharmony_ci  }
10711cb0ef41Sopenharmony_ci  // Execute the stack transfers before filling the instance register.
10721cb0ef41Sopenharmony_ci  stack_transfers.Execute();
10731cb0ef41Sopenharmony_ci  // Pop parameters from the value stack.
10741cb0ef41Sopenharmony_ci  cache_state_.stack_state.pop_back(num_params);
10751cb0ef41Sopenharmony_ci
10761cb0ef41Sopenharmony_ci  // Reset register use counters.
10771cb0ef41Sopenharmony_ci  cache_state_.reset_used_registers();
10781cb0ef41Sopenharmony_ci
10791cb0ef41Sopenharmony_ci  // Reload the instance from the stack.
10801cb0ef41Sopenharmony_ci  if (!target_instance) {
10811cb0ef41Sopenharmony_ci    LoadInstanceFromFrame(instance_reg);
10821cb0ef41Sopenharmony_ci  }
10831cb0ef41Sopenharmony_ci}
10841cb0ef41Sopenharmony_ci
10851cb0ef41Sopenharmony_civoid LiftoffAssembler::FinishCall(const ValueKindSig* sig,
10861cb0ef41Sopenharmony_ci                                  compiler::CallDescriptor* call_descriptor) {
10871cb0ef41Sopenharmony_ci  int call_desc_return_idx = 0;
10881cb0ef41Sopenharmony_ci  for (ValueKind return_kind : sig->returns()) {
10891cb0ef41Sopenharmony_ci    DCHECK_LT(call_desc_return_idx, call_descriptor->ReturnCount());
10901cb0ef41Sopenharmony_ci    const bool needs_gp_pair = needs_gp_reg_pair(return_kind);
10911cb0ef41Sopenharmony_ci    const int num_lowered_params = 1 + needs_gp_pair;
10921cb0ef41Sopenharmony_ci    const ValueKind lowered_kind = needs_gp_pair ? kI32 : return_kind;
10931cb0ef41Sopenharmony_ci    const RegClass rc = reg_class_for(lowered_kind);
10941cb0ef41Sopenharmony_ci    // Initialize to anything, will be set in the loop and used afterwards.
10951cb0ef41Sopenharmony_ci    LiftoffRegister reg_pair[2] = {kGpCacheRegList.GetFirstRegSet(),
10961cb0ef41Sopenharmony_ci                                   kGpCacheRegList.GetFirstRegSet()};
10971cb0ef41Sopenharmony_ci    LiftoffRegList pinned;
10981cb0ef41Sopenharmony_ci    for (int pair_idx = 0; pair_idx < num_lowered_params; ++pair_idx) {
10991cb0ef41Sopenharmony_ci      compiler::LinkageLocation loc =
11001cb0ef41Sopenharmony_ci          call_descriptor->GetReturnLocation(call_desc_return_idx++);
11011cb0ef41Sopenharmony_ci      if (loc.IsRegister()) {
11021cb0ef41Sopenharmony_ci        DCHECK(!loc.IsAnyRegister());
11031cb0ef41Sopenharmony_ci        reg_pair[pair_idx] = LiftoffRegister::from_external_code(
11041cb0ef41Sopenharmony_ci            rc, lowered_kind, loc.AsRegister());
11051cb0ef41Sopenharmony_ci      } else {
11061cb0ef41Sopenharmony_ci        DCHECK(loc.IsCallerFrameSlot());
11071cb0ef41Sopenharmony_ci        reg_pair[pair_idx] = GetUnusedRegister(rc, pinned);
11081cb0ef41Sopenharmony_ci        // Get slot offset relative to the stack pointer.
11091cb0ef41Sopenharmony_ci        int offset = call_descriptor->GetOffsetToReturns();
11101cb0ef41Sopenharmony_ci        int return_slot = -loc.GetLocation() - offset - 1;
11111cb0ef41Sopenharmony_ci        LoadReturnStackSlot(reg_pair[pair_idx],
11121cb0ef41Sopenharmony_ci                            return_slot * kSystemPointerSize, lowered_kind);
11131cb0ef41Sopenharmony_ci      }
11141cb0ef41Sopenharmony_ci      if (pair_idx == 0) {
11151cb0ef41Sopenharmony_ci        pinned.set(reg_pair[0]);
11161cb0ef41Sopenharmony_ci      }
11171cb0ef41Sopenharmony_ci    }
11181cb0ef41Sopenharmony_ci    if (num_lowered_params == 1) {
11191cb0ef41Sopenharmony_ci      PushRegister(return_kind, reg_pair[0]);
11201cb0ef41Sopenharmony_ci    } else {
11211cb0ef41Sopenharmony_ci      PushRegister(return_kind, LiftoffRegister::ForPair(reg_pair[0].gp(),
11221cb0ef41Sopenharmony_ci                                                         reg_pair[1].gp()));
11231cb0ef41Sopenharmony_ci    }
11241cb0ef41Sopenharmony_ci  }
11251cb0ef41Sopenharmony_ci  int return_slots = static_cast<int>(call_descriptor->ReturnSlotCount());
11261cb0ef41Sopenharmony_ci  RecordUsedSpillOffset(TopSpillOffset() + return_slots * kSystemPointerSize);
11271cb0ef41Sopenharmony_ci}
11281cb0ef41Sopenharmony_ci
11291cb0ef41Sopenharmony_civoid LiftoffAssembler::Move(LiftoffRegister dst, LiftoffRegister src,
11301cb0ef41Sopenharmony_ci                            ValueKind kind) {
11311cb0ef41Sopenharmony_ci  DCHECK_EQ(dst.reg_class(), src.reg_class());
11321cb0ef41Sopenharmony_ci  DCHECK_NE(dst, src);
11331cb0ef41Sopenharmony_ci  if (kNeedI64RegPair && dst.is_gp_pair()) {
11341cb0ef41Sopenharmony_ci    // Use the {StackTransferRecipe} to move pairs, as the registers in the
11351cb0ef41Sopenharmony_ci    // pairs might overlap.
11361cb0ef41Sopenharmony_ci    StackTransferRecipe(this).MoveRegister(dst, src, kind);
11371cb0ef41Sopenharmony_ci  } else if (kNeedS128RegPair && dst.is_fp_pair()) {
11381cb0ef41Sopenharmony_ci    // Calling low_fp is fine, Move will automatically check the kind and
11391cb0ef41Sopenharmony_ci    // convert this FP to its SIMD register, and use a SIMD move.
11401cb0ef41Sopenharmony_ci    Move(dst.low_fp(), src.low_fp(), kind);
11411cb0ef41Sopenharmony_ci  } else if (dst.is_gp()) {
11421cb0ef41Sopenharmony_ci    Move(dst.gp(), src.gp(), kind);
11431cb0ef41Sopenharmony_ci  } else {
11441cb0ef41Sopenharmony_ci    Move(dst.fp(), src.fp(), kind);
11451cb0ef41Sopenharmony_ci  }
11461cb0ef41Sopenharmony_ci}
11471cb0ef41Sopenharmony_ci
11481cb0ef41Sopenharmony_civoid LiftoffAssembler::ParallelRegisterMove(
11491cb0ef41Sopenharmony_ci    base::Vector<const ParallelRegisterMoveTuple> tuples) {
11501cb0ef41Sopenharmony_ci  StackTransferRecipe stack_transfers(this);
11511cb0ef41Sopenharmony_ci  for (auto tuple : tuples) {
11521cb0ef41Sopenharmony_ci    if (tuple.dst == tuple.src) continue;
11531cb0ef41Sopenharmony_ci    stack_transfers.MoveRegister(tuple.dst, tuple.src, tuple.kind);
11541cb0ef41Sopenharmony_ci  }
11551cb0ef41Sopenharmony_ci}
11561cb0ef41Sopenharmony_ci
11571cb0ef41Sopenharmony_civoid LiftoffAssembler::MoveToReturnLocations(
11581cb0ef41Sopenharmony_ci    const FunctionSig* sig, compiler::CallDescriptor* descriptor) {
11591cb0ef41Sopenharmony_ci  StackTransferRecipe stack_transfers(this);
11601cb0ef41Sopenharmony_ci  if (sig->return_count() == 1) {
11611cb0ef41Sopenharmony_ci    ValueKind return_kind = sig->GetReturn(0).kind();
11621cb0ef41Sopenharmony_ci    // Defaults to a gp reg, will be set below if return kind is not gp.
11631cb0ef41Sopenharmony_ci    LiftoffRegister return_reg = LiftoffRegister(kGpReturnRegisters[0]);
11641cb0ef41Sopenharmony_ci
11651cb0ef41Sopenharmony_ci    if (needs_gp_reg_pair(return_kind)) {
11661cb0ef41Sopenharmony_ci      return_reg = LiftoffRegister::ForPair(kGpReturnRegisters[0],
11671cb0ef41Sopenharmony_ci                                            kGpReturnRegisters[1]);
11681cb0ef41Sopenharmony_ci    } else if (needs_fp_reg_pair(return_kind)) {
11691cb0ef41Sopenharmony_ci      return_reg = LiftoffRegister::ForFpPair(kFpReturnRegisters[0]);
11701cb0ef41Sopenharmony_ci    } else if (reg_class_for(return_kind) == kFpReg) {
11711cb0ef41Sopenharmony_ci      return_reg = LiftoffRegister(kFpReturnRegisters[0]);
11721cb0ef41Sopenharmony_ci    } else {
11731cb0ef41Sopenharmony_ci      DCHECK_EQ(kGpReg, reg_class_for(return_kind));
11741cb0ef41Sopenharmony_ci    }
11751cb0ef41Sopenharmony_ci    stack_transfers.LoadIntoRegister(return_reg,
11761cb0ef41Sopenharmony_ci                                     cache_state_.stack_state.back(),
11771cb0ef41Sopenharmony_ci                                     cache_state_.stack_state.back().offset());
11781cb0ef41Sopenharmony_ci    return;
11791cb0ef41Sopenharmony_ci  }
11801cb0ef41Sopenharmony_ci
11811cb0ef41Sopenharmony_ci  // Slow path for multi-return.
11821cb0ef41Sopenharmony_ci  // We sometimes allocate a register to perform stack-to-stack moves, which can
11831cb0ef41Sopenharmony_ci  // cause a spill in the cache state. Conservatively save and restore the
11841cb0ef41Sopenharmony_ci  // original state in case it is needed after the current instruction
11851cb0ef41Sopenharmony_ci  // (conditional branch).
11861cb0ef41Sopenharmony_ci  CacheState saved_state;
11871cb0ef41Sopenharmony_ci  saved_state.Split(*cache_state());
11881cb0ef41Sopenharmony_ci  int call_desc_return_idx = 0;
11891cb0ef41Sopenharmony_ci  DCHECK_LE(sig->return_count(), cache_state_.stack_height());
11901cb0ef41Sopenharmony_ci  VarState* slots = cache_state_.stack_state.end() - sig->return_count();
11911cb0ef41Sopenharmony_ci  // Fill return frame slots first to ensure that all potential spills happen
11921cb0ef41Sopenharmony_ci  // before we prepare the stack transfers.
11931cb0ef41Sopenharmony_ci  for (size_t i = 0; i < sig->return_count(); ++i) {
11941cb0ef41Sopenharmony_ci    ValueKind return_kind = sig->GetReturn(i).kind();
11951cb0ef41Sopenharmony_ci    bool needs_gp_pair = needs_gp_reg_pair(return_kind);
11961cb0ef41Sopenharmony_ci    int num_lowered_params = 1 + needs_gp_pair;
11971cb0ef41Sopenharmony_ci    for (int pair_idx = 0; pair_idx < num_lowered_params; ++pair_idx) {
11981cb0ef41Sopenharmony_ci      compiler::LinkageLocation loc =
11991cb0ef41Sopenharmony_ci          descriptor->GetReturnLocation(call_desc_return_idx++);
12001cb0ef41Sopenharmony_ci      if (loc.IsCallerFrameSlot()) {
12011cb0ef41Sopenharmony_ci        RegPairHalf half = pair_idx == 0 ? kLowWord : kHighWord;
12021cb0ef41Sopenharmony_ci        VarState& slot = slots[i];
12031cb0ef41Sopenharmony_ci        LiftoffRegister reg = needs_gp_pair
12041cb0ef41Sopenharmony_ci                                  ? LoadI64HalfIntoRegister(slot, half)
12051cb0ef41Sopenharmony_ci                                  : LoadToRegister(slot, {});
12061cb0ef41Sopenharmony_ci        ValueKind lowered_kind = needs_gp_pair ? kI32 : return_kind;
12071cb0ef41Sopenharmony_ci        StoreCallerFrameSlot(reg, -loc.AsCallerFrameSlot(), lowered_kind);
12081cb0ef41Sopenharmony_ci      }
12091cb0ef41Sopenharmony_ci    }
12101cb0ef41Sopenharmony_ci  }
12111cb0ef41Sopenharmony_ci  // Prepare and execute stack transfers.
12121cb0ef41Sopenharmony_ci  call_desc_return_idx = 0;
12131cb0ef41Sopenharmony_ci  for (size_t i = 0; i < sig->return_count(); ++i) {
12141cb0ef41Sopenharmony_ci    ValueKind return_kind = sig->GetReturn(i).kind();
12151cb0ef41Sopenharmony_ci    bool needs_gp_pair = needs_gp_reg_pair(return_kind);
12161cb0ef41Sopenharmony_ci    int num_lowered_params = 1 + needs_gp_pair;
12171cb0ef41Sopenharmony_ci    for (int pair_idx = 0; pair_idx < num_lowered_params; ++pair_idx) {
12181cb0ef41Sopenharmony_ci      RegPairHalf half = pair_idx == 0 ? kLowWord : kHighWord;
12191cb0ef41Sopenharmony_ci      compiler::LinkageLocation loc =
12201cb0ef41Sopenharmony_ci          descriptor->GetReturnLocation(call_desc_return_idx++);
12211cb0ef41Sopenharmony_ci      if (loc.IsRegister()) {
12221cb0ef41Sopenharmony_ci        DCHECK(!loc.IsAnyRegister());
12231cb0ef41Sopenharmony_ci        int reg_code = loc.AsRegister();
12241cb0ef41Sopenharmony_ci        ValueKind lowered_kind = needs_gp_pair ? kI32 : return_kind;
12251cb0ef41Sopenharmony_ci        RegClass rc = reg_class_for(lowered_kind);
12261cb0ef41Sopenharmony_ci        LiftoffRegister reg =
12271cb0ef41Sopenharmony_ci            LiftoffRegister::from_external_code(rc, return_kind, reg_code);
12281cb0ef41Sopenharmony_ci        VarState& slot = slots[i];
12291cb0ef41Sopenharmony_ci        if (needs_gp_pair) {
12301cb0ef41Sopenharmony_ci          stack_transfers.LoadI64HalfIntoRegister(reg, slot, slot.offset(),
12311cb0ef41Sopenharmony_ci                                                  half);
12321cb0ef41Sopenharmony_ci        } else {
12331cb0ef41Sopenharmony_ci          stack_transfers.LoadIntoRegister(reg, slot, slot.offset());
12341cb0ef41Sopenharmony_ci        }
12351cb0ef41Sopenharmony_ci      }
12361cb0ef41Sopenharmony_ci    }
12371cb0ef41Sopenharmony_ci  }
12381cb0ef41Sopenharmony_ci  cache_state()->Steal(saved_state);
12391cb0ef41Sopenharmony_ci}
12401cb0ef41Sopenharmony_ci
12411cb0ef41Sopenharmony_ci#ifdef ENABLE_SLOW_DCHECKS
12421cb0ef41Sopenharmony_cibool LiftoffAssembler::ValidateCacheState() const {
12431cb0ef41Sopenharmony_ci  uint32_t register_use_count[kAfterMaxLiftoffRegCode] = {0};
12441cb0ef41Sopenharmony_ci  LiftoffRegList used_regs;
12451cb0ef41Sopenharmony_ci  for (const VarState& var : cache_state_.stack_state) {
12461cb0ef41Sopenharmony_ci    if (!var.is_reg()) continue;
12471cb0ef41Sopenharmony_ci    LiftoffRegister reg = var.reg();
12481cb0ef41Sopenharmony_ci    if ((kNeedI64RegPair || kNeedS128RegPair) && reg.is_pair()) {
12491cb0ef41Sopenharmony_ci      ++register_use_count[reg.low().liftoff_code()];
12501cb0ef41Sopenharmony_ci      ++register_use_count[reg.high().liftoff_code()];
12511cb0ef41Sopenharmony_ci    } else {
12521cb0ef41Sopenharmony_ci      ++register_use_count[reg.liftoff_code()];
12531cb0ef41Sopenharmony_ci    }
12541cb0ef41Sopenharmony_ci    used_regs.set(reg);
12551cb0ef41Sopenharmony_ci  }
12561cb0ef41Sopenharmony_ci  for (Register cache_reg :
12571cb0ef41Sopenharmony_ci       {cache_state_.cached_instance, cache_state_.cached_mem_start}) {
12581cb0ef41Sopenharmony_ci    if (cache_reg != no_reg) {
12591cb0ef41Sopenharmony_ci      DCHECK(!used_regs.has(cache_reg));
12601cb0ef41Sopenharmony_ci      int liftoff_code = LiftoffRegister{cache_reg}.liftoff_code();
12611cb0ef41Sopenharmony_ci      used_regs.set(cache_reg);
12621cb0ef41Sopenharmony_ci      DCHECK_EQ(0, register_use_count[liftoff_code]);
12631cb0ef41Sopenharmony_ci      register_use_count[liftoff_code] = 1;
12641cb0ef41Sopenharmony_ci    }
12651cb0ef41Sopenharmony_ci  }
12661cb0ef41Sopenharmony_ci  bool valid = memcmp(register_use_count, cache_state_.register_use_count,
12671cb0ef41Sopenharmony_ci                      sizeof(register_use_count)) == 0 &&
12681cb0ef41Sopenharmony_ci               used_regs == cache_state_.used_registers;
12691cb0ef41Sopenharmony_ci  if (valid) return true;
12701cb0ef41Sopenharmony_ci  std::ostringstream os;
12711cb0ef41Sopenharmony_ci  os << "Error in LiftoffAssembler::ValidateCacheState().\n";
12721cb0ef41Sopenharmony_ci  os << "expected: used_regs " << used_regs << ", counts "
12731cb0ef41Sopenharmony_ci     << PrintCollection(register_use_count) << "\n";
12741cb0ef41Sopenharmony_ci  os << "found:    used_regs " << cache_state_.used_registers << ", counts "
12751cb0ef41Sopenharmony_ci     << PrintCollection(cache_state_.register_use_count) << "\n";
12761cb0ef41Sopenharmony_ci  os << "Use --trace-wasm-decoder and --trace-liftoff to debug.";
12771cb0ef41Sopenharmony_ci  FATAL("%s", os.str().c_str());
12781cb0ef41Sopenharmony_ci}
12791cb0ef41Sopenharmony_ci#endif
12801cb0ef41Sopenharmony_ci
12811cb0ef41Sopenharmony_ciLiftoffRegister LiftoffAssembler::SpillOneRegister(LiftoffRegList candidates) {
12821cb0ef41Sopenharmony_ci  // Spill one cached value to free a register.
12831cb0ef41Sopenharmony_ci  LiftoffRegister spill_reg = cache_state_.GetNextSpillReg(candidates);
12841cb0ef41Sopenharmony_ci  SpillRegister(spill_reg);
12851cb0ef41Sopenharmony_ci  return spill_reg;
12861cb0ef41Sopenharmony_ci}
12871cb0ef41Sopenharmony_ci
12881cb0ef41Sopenharmony_ciLiftoffRegister LiftoffAssembler::SpillAdjacentFpRegisters(
12891cb0ef41Sopenharmony_ci    LiftoffRegList pinned) {
12901cb0ef41Sopenharmony_ci  // We end up in this call only when:
12911cb0ef41Sopenharmony_ci  // [1] kNeedS128RegPair, and
12921cb0ef41Sopenharmony_ci  // [2] there are no pair of adjacent FP registers that are free
12931cb0ef41Sopenharmony_ci  CHECK(kNeedS128RegPair);
12941cb0ef41Sopenharmony_ci  DCHECK(!kFpCacheRegList.MaskOut(pinned)
12951cb0ef41Sopenharmony_ci              .MaskOut(cache_state_.used_registers)
12961cb0ef41Sopenharmony_ci              .HasAdjacentFpRegsSet());
12971cb0ef41Sopenharmony_ci
12981cb0ef41Sopenharmony_ci  // Special logic, if the top fp register is even, we might hit a case of an
12991cb0ef41Sopenharmony_ci  // invalid register in case 2.
13001cb0ef41Sopenharmony_ci  LiftoffRegister last_fp = kFpCacheRegList.GetLastRegSet();
13011cb0ef41Sopenharmony_ci  if (last_fp.fp().code() % 2 == 0) {
13021cb0ef41Sopenharmony_ci    pinned.set(last_fp);
13031cb0ef41Sopenharmony_ci  }
13041cb0ef41Sopenharmony_ci
13051cb0ef41Sopenharmony_ci  // We can try to optimize the spilling here:
13061cb0ef41Sopenharmony_ci  // 1. Try to get a free fp register, either:
13071cb0ef41Sopenharmony_ci  //  a. This register is already free, or
13081cb0ef41Sopenharmony_ci  //  b. it had to be spilled.
13091cb0ef41Sopenharmony_ci  // 2. If 1a, the adjacent register is used (invariant [2]), spill it.
13101cb0ef41Sopenharmony_ci  // 3. If 1b, check the adjacent register:
13111cb0ef41Sopenharmony_ci  //  a. If free, done!
13121cb0ef41Sopenharmony_ci  //  b. If used, spill it.
13131cb0ef41Sopenharmony_ci  // We spill one register in 2 and 3a, and two registers in 3b.
13141cb0ef41Sopenharmony_ci
13151cb0ef41Sopenharmony_ci  LiftoffRegister first_reg = GetUnusedRegister(kFpReg, pinned);
13161cb0ef41Sopenharmony_ci  LiftoffRegister second_reg = first_reg, low_reg = first_reg;
13171cb0ef41Sopenharmony_ci
13181cb0ef41Sopenharmony_ci  if (first_reg.fp().code() % 2 == 0) {
13191cb0ef41Sopenharmony_ci    second_reg =
13201cb0ef41Sopenharmony_ci        LiftoffRegister::from_liftoff_code(first_reg.liftoff_code() + 1);
13211cb0ef41Sopenharmony_ci  } else {
13221cb0ef41Sopenharmony_ci    second_reg =
13231cb0ef41Sopenharmony_ci        LiftoffRegister::from_liftoff_code(first_reg.liftoff_code() - 1);
13241cb0ef41Sopenharmony_ci    low_reg = second_reg;
13251cb0ef41Sopenharmony_ci  }
13261cb0ef41Sopenharmony_ci
13271cb0ef41Sopenharmony_ci  if (cache_state_.is_used(second_reg)) {
13281cb0ef41Sopenharmony_ci    SpillRegister(second_reg);
13291cb0ef41Sopenharmony_ci  }
13301cb0ef41Sopenharmony_ci
13311cb0ef41Sopenharmony_ci  return low_reg;
13321cb0ef41Sopenharmony_ci}
13331cb0ef41Sopenharmony_ci
13341cb0ef41Sopenharmony_civoid LiftoffAssembler::SpillRegister(LiftoffRegister reg) {
13351cb0ef41Sopenharmony_ci  int remaining_uses = cache_state_.get_use_count(reg);
13361cb0ef41Sopenharmony_ci  DCHECK_LT(0, remaining_uses);
13371cb0ef41Sopenharmony_ci  for (uint32_t idx = cache_state_.stack_height() - 1;; --idx) {
13381cb0ef41Sopenharmony_ci    DCHECK_GT(cache_state_.stack_height(), idx);
13391cb0ef41Sopenharmony_ci    auto* slot = &cache_state_.stack_state[idx];
13401cb0ef41Sopenharmony_ci    if (!slot->is_reg() || !slot->reg().overlaps(reg)) continue;
13411cb0ef41Sopenharmony_ci    if (slot->reg().is_pair()) {
13421cb0ef41Sopenharmony_ci      // Make sure to decrement *both* registers in a pair, because the
13431cb0ef41Sopenharmony_ci      // {clear_used} call below only clears one of them.
13441cb0ef41Sopenharmony_ci      cache_state_.dec_used(slot->reg().low());
13451cb0ef41Sopenharmony_ci      cache_state_.dec_used(slot->reg().high());
13461cb0ef41Sopenharmony_ci      cache_state_.last_spilled_regs.set(slot->reg().low());
13471cb0ef41Sopenharmony_ci      cache_state_.last_spilled_regs.set(slot->reg().high());
13481cb0ef41Sopenharmony_ci    }
13491cb0ef41Sopenharmony_ci    Spill(slot->offset(), slot->reg(), slot->kind());
13501cb0ef41Sopenharmony_ci    slot->MakeStack();
13511cb0ef41Sopenharmony_ci    if (--remaining_uses == 0) break;
13521cb0ef41Sopenharmony_ci  }
13531cb0ef41Sopenharmony_ci  cache_state_.clear_used(reg);
13541cb0ef41Sopenharmony_ci  cache_state_.last_spilled_regs.set(reg);
13551cb0ef41Sopenharmony_ci}
13561cb0ef41Sopenharmony_ci
13571cb0ef41Sopenharmony_civoid LiftoffAssembler::set_num_locals(uint32_t num_locals) {
13581cb0ef41Sopenharmony_ci  DCHECK_EQ(0, num_locals_);  // only call this once.
13591cb0ef41Sopenharmony_ci  num_locals_ = num_locals;
13601cb0ef41Sopenharmony_ci  if (num_locals > kInlineLocalKinds) {
13611cb0ef41Sopenharmony_ci    more_local_kinds_ = reinterpret_cast<ValueKind*>(
13621cb0ef41Sopenharmony_ci        base::Malloc(num_locals * sizeof(ValueKind)));
13631cb0ef41Sopenharmony_ci    DCHECK_NOT_NULL(more_local_kinds_);
13641cb0ef41Sopenharmony_ci  }
13651cb0ef41Sopenharmony_ci}
13661cb0ef41Sopenharmony_ci
13671cb0ef41Sopenharmony_cistd::ostream& operator<<(std::ostream& os, VarState slot) {
13681cb0ef41Sopenharmony_ci  os << name(slot.kind()) << ":";
13691cb0ef41Sopenharmony_ci  switch (slot.loc()) {
13701cb0ef41Sopenharmony_ci    case VarState::kStack:
13711cb0ef41Sopenharmony_ci      return os << "s0x" << std::hex << slot.offset() << std::dec;
13721cb0ef41Sopenharmony_ci    case VarState::kRegister:
13731cb0ef41Sopenharmony_ci      return os << slot.reg();
13741cb0ef41Sopenharmony_ci    case VarState::kIntConst:
13751cb0ef41Sopenharmony_ci      return os << "c" << slot.i32_const();
13761cb0ef41Sopenharmony_ci  }
13771cb0ef41Sopenharmony_ci  UNREACHABLE();
13781cb0ef41Sopenharmony_ci}
13791cb0ef41Sopenharmony_ci
13801cb0ef41Sopenharmony_ci#if DEBUG
13811cb0ef41Sopenharmony_cibool CheckCompatibleStackSlotTypes(ValueKind a, ValueKind b) {
13821cb0ef41Sopenharmony_ci  if (is_object_reference(a)) {
13831cb0ef41Sopenharmony_ci    // Since Liftoff doesn't do accurate type tracking (e.g. on loop back
13841cb0ef41Sopenharmony_ci    // edges), we only care that pointer types stay amongst pointer types.
13851cb0ef41Sopenharmony_ci    // It's fine if ref/optref overwrite each other.
13861cb0ef41Sopenharmony_ci    DCHECK(is_object_reference(b));
13871cb0ef41Sopenharmony_ci  } else if (is_rtt(a)) {
13881cb0ef41Sopenharmony_ci    // Same for rtt/rtt_with_depth.
13891cb0ef41Sopenharmony_ci    DCHECK(is_rtt(b));
13901cb0ef41Sopenharmony_ci  } else {
13911cb0ef41Sopenharmony_ci    // All other types (primitive numbers, bottom/stmt) must be equal.
13921cb0ef41Sopenharmony_ci    DCHECK_EQ(a, b);
13931cb0ef41Sopenharmony_ci  }
13941cb0ef41Sopenharmony_ci  return true;  // Dummy so this can be called via DCHECK.
13951cb0ef41Sopenharmony_ci}
13961cb0ef41Sopenharmony_ci#endif
13971cb0ef41Sopenharmony_ci
13981cb0ef41Sopenharmony_ci}  // namespace wasm
13991cb0ef41Sopenharmony_ci}  // namespace internal
14001cb0ef41Sopenharmony_ci}  // namespace v8
1401