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*>(®ister_moves_) + 2721cb0ef41Sopenharmony_ci reg.liftoff_code(); 2731cb0ef41Sopenharmony_ci } 2741cb0ef41Sopenharmony_ci RegisterLoad* register_load(LiftoffRegister reg) { 2751cb0ef41Sopenharmony_ci return reinterpret_cast<RegisterLoad*>(®ister_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, ¶m_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, ¶m_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