/*
 * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved.
 * Copyright (c) 2020, 2022, Huawei Technologies Co., Ltd. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 *
 */

#ifndef CPU_RISCV_C2_MACROASSEMBLER_RISCV_HPP
#define CPU_RISCV_C2_MACROASSEMBLER_RISCV_HPP

// C2_MacroAssembler contains high-level macros for C2

 private:
  // Return true if the phase output is in the scratch emit size mode.
  virtual bool in_scratch_emit_size() override;

  void element_compare(Register r1, Register r2,
                       Register result, Register cnt,
                       Register tmp1, Register tmp2,
                       VectorRegister vr1, VectorRegister vr2,
                       VectorRegister vrs,
                       bool is_latin, Label& DONE, Assembler::LMUL lmul);

  void string_compare_long_same_encoding(Register result, Register str1, Register str2,
                                  const bool isLL, Register cnt1, Register cnt2,
                                  Register tmp1, Register tmp2, Register tmp3,
                                  const int STUB_THRESHOLD, Label *STUB, Label *SHORT_STRING, Label *DONE);
  void string_compare_long_different_encoding(Register result, Register str1, Register str2,
                                  bool isLU, Register cnt1, Register cnt2,
                                  Register tmp1, Register tmp2, Register tmp3,
                                  const int STUB_THRESHOLD, Label *STUB, Label *DONE);

 public:
  // Code used by cmpFastLock and cmpFastUnlock mach instructions in .ad file.
  void fast_lock(Register object, Register box,
                 Register tmp1, Register tmp2, Register tmp3, Register tmp4);
  void fast_unlock(Register object, Register box, Register tmp1, Register tmp2);

  // Code used by cmpFastLockLightweight and cmpFastUnlockLightweight mach instructions in .ad file.
  void fast_lock_lightweight(Register object, Register box,
                             Register tmp1, Register tmp2, Register tmp3, Register tmp4);
  void fast_unlock_lightweight(Register object, Register box,
                               Register tmp1, Register tmp2, Register tmp3);

  void string_compare(Register str1, Register str2,
                      Register cnt1, Register cnt2, Register result,
                      Register tmp1, Register tmp2, Register tmp3,
                      int ae);

  void string_indexof_char_short(Register str1, Register cnt1,
                                 Register ch, Register result,
                                 bool isL);

  void string_indexof_char(Register str1, Register cnt1,
                           Register ch, Register result,
                           Register tmp1, Register tmp2,
                           Register tmp3, Register tmp4,
                           bool isL);

  void string_indexof(Register str1, Register str2,
                      Register cnt1, Register cnt2,
                      Register tmp1, Register tmp2,
                      Register tmp3, Register tmp4,
                      Register tmp5, Register tmp6,
                      Register result, int ae);

  void string_indexof_linearscan(Register haystack, Register needle,
                                 Register haystack_len, Register needle_len,
                                 Register tmp1, Register tmp2,
                                 Register tmp3, Register tmp4,
                                 int needle_con_cnt, Register result, int ae);

  void arrays_equals(Register r1, Register r2,
                     Register tmp1, Register tmp2, Register tmp3,
                     Register result, int elem_size);

  void arrays_hashcode(Register ary, Register cnt, Register result,
                       Register tmp1, Register tmp2,
                       Register tmp3, Register tmp4,
                       Register tmp5, Register tmp6,
                       BasicType eltype);

  // helper function for arrays_hashcode
  int arrays_hashcode_elsize(BasicType eltype);
  void arrays_hashcode_elload(Register dst, Address src, BasicType eltype);

  void string_equals(Register r1, Register r2,
                     Register result, Register cnt1);

  // refer to conditional_branches and float_conditional_branches
  static const int bool_test_bits = 3;
  static const int unsigned_branch_mask = 1 << bool_test_bits;
  static const int double_branch_mask = 1 << bool_test_bits;

  // cmp
  void cmp_branch(int cmpFlag,
                  Register op1, Register op2,
                  Label& label, bool is_far = false);

  void float_cmp_branch(int cmpFlag,
                        FloatRegister op1, FloatRegister op2,
                        Label& label, bool is_far = false);

  void enc_cmpUEqNeLeGt_imm0_branch(int cmpFlag, Register op,
                                    Label& L, bool is_far = false);

  void enc_cmpEqNe_imm0_branch(int cmpFlag, Register op,
                               Label& L, bool is_far = false);

  void enc_cmove(int cmpFlag,
                 Register op1, Register op2,
                 Register dst, Register src);

  void enc_cmove_cmp_fp(int cmpFlag,
                        FloatRegister op1, FloatRegister op2,
                        Register dst, Register src, bool is_single);

  void spill(Register r, bool is64, int offset) {
    is64 ? sd(r, Address(sp, offset))
         : sw(r, Address(sp, offset));
  }

  void spill(FloatRegister f, bool is64, int offset) {
    is64 ? fsd(f, Address(sp, offset))
         : fsw(f, Address(sp, offset));
  }

  void spill(VectorRegister v, int offset) {
    add(t0, sp, offset);
    vs1r_v(v, t0);
  }

  void unspill(Register r, bool is64, int offset) {
    is64 ? ld(r, Address(sp, offset))
         : lw(r, Address(sp, offset));
  }

  void unspillu(Register r, bool is64, int offset) {
    is64 ? ld(r, Address(sp, offset))
         : lwu(r, Address(sp, offset));
  }

  void unspill(FloatRegister f, bool is64, int offset) {
    is64 ? fld(f, Address(sp, offset))
         : flw(f, Address(sp, offset));
  }

  void unspill(VectorRegister v, int offset) {
    add(t0, sp, offset);
    vl1r_v(v, t0);
  }

  void spill_copy_vector_stack_to_stack(int src_offset, int dst_offset, uint vector_length_in_bytes) {
    assert(vector_length_in_bytes % 16 == 0, "unexpected vector reg size");
    for (int i = 0; i < (int)vector_length_in_bytes / 8; i++) {
      unspill(t0, true, src_offset + (i * 8));
      spill(t0, true, dst_offset + (i * 8));
    }
  }

  enum class FLOAT_TYPE {
    half_precision,
    single_precision,
    double_precision
  };

  void minmax_fp(FloatRegister dst,
                 FloatRegister src1, FloatRegister src2,
                 FLOAT_TYPE ft, bool is_min);

  void round_double_mode(FloatRegister dst, FloatRegister src, int round_mode,
                         Register tmp1, Register tmp2, Register tmp3);

  void signum_fp(FloatRegister dst, FloatRegister one, bool is_double);

  void float16_to_float(FloatRegister dst, Register src, Register tmp);
  void float_to_float16(Register dst, FloatRegister src, FloatRegister ftmp, Register xtmp);

  void signum_fp_v(VectorRegister dst, VectorRegister one, BasicType bt, int vlen);


  // intrinsic methods implemented by rvv instructions

  void java_round_float_v(VectorRegister dst, VectorRegister src, FloatRegister ftmp, BasicType bt, uint vector_length);
  void java_round_double_v(VectorRegister dst, VectorRegister src, FloatRegister ftmp, BasicType bt, uint vector_length);

  void float16_to_float_v(VectorRegister dst, VectorRegister src, uint vector_length);
  void float_to_float16_v(VectorRegister dst, VectorRegister src, VectorRegister vtmp, Register tmp, uint vector_length);

  void string_equals_v(Register r1, Register r2,
                       Register result, Register cnt1);

  void arrays_equals_v(Register r1, Register r2,
                       Register result, Register cnt1,
                       int elem_size);

  void string_compare_v(Register str1, Register str2,
                        Register cnt1, Register cnt2,
                        Register result,
                        Register tmp1, Register tmp2,
                        int encForm);

  void clear_array_v(Register base, Register cnt);

  void byte_array_inflate_v(Register src, Register dst,
                            Register len, Register tmp);

  void char_array_compress_v(Register src, Register dst,
                            Register len, Register result,
                            Register tmp);

  void encode_iso_array_v(Register src, Register dst,
                          Register len, Register result,
                          Register tmp, bool ascii);

  void count_positives_v(Register ary, Register len,
                        Register result, Register tmp);

  void string_indexof_char_v(Register str1, Register cnt1,
                            Register ch, Register result,
                            Register tmp1, Register tmp2,
                            bool isL);

  void minmax_fp_v(VectorRegister dst,
                  VectorRegister src1, VectorRegister src2,
                  BasicType bt, bool is_min, uint vector_length);

  void minmax_fp_masked_v(VectorRegister dst, VectorRegister src1, VectorRegister src2,
                          VectorRegister vmask, VectorRegister tmp1, VectorRegister tmp2,
                          BasicType bt, bool is_min, uint vector_length);

  void reduce_minmax_fp_v(FloatRegister dst,
                          FloatRegister src1, VectorRegister src2,
                          VectorRegister tmp1, VectorRegister tmp2,
                          bool is_double, bool is_min, uint vector_length,
                          VectorMask vm = Assembler::unmasked);

  void reduce_integral_v(Register dst, Register src1,
                        VectorRegister src2, VectorRegister tmp,
                        int opc, BasicType bt, uint vector_length,
                        VectorMask vm = Assembler::unmasked);

  void reduce_mul_integral_v(Register dst, Register src1, VectorRegister src2,
                             VectorRegister vtmp1, VectorRegister vtmp2, BasicType bt,
                             uint vector_length, VectorMask vm = Assembler::unmasked);

  void vsetvli_helper(BasicType bt, uint vector_length, LMUL vlmul = Assembler::m1, Register tmp = t0);

  void compare_integral_v(VectorRegister dst, VectorRegister src1, VectorRegister src2, int cond,
                          BasicType bt, uint vector_length, VectorMask vm = Assembler::unmasked);

  void compare_fp_v(VectorRegister dst, VectorRegister src1, VectorRegister src2, int cond,
                    BasicType bt, uint vector_length, VectorMask vm = Assembler::unmasked);

  void spill_vmask(VectorRegister v, int offset);

  void unspill_vmask(VectorRegister v, int offset);

  void spill_copy_vmask_stack_to_stack(int src_offset, int dst_offset, uint vector_length_in_bytes) {
    assert(vector_length_in_bytes % 4 == 0, "unexpected vector mask reg size");
    for (int i = 0; i < (int)vector_length_in_bytes / 4; i++) {
      unspill(t0, false, src_offset + (i * 4));
      spill(t0, false, dst_offset + (i * 4));
    }
  }

  void integer_extend_v(VectorRegister dst, BasicType dst_bt, uint vector_length,
                        VectorRegister src, BasicType src_bt, bool is_signed);

  void integer_narrow_v(VectorRegister dst, BasicType dst_bt, uint vector_length,
                        VectorRegister src, BasicType src_bt);

  void vfcvt_rtz_x_f_v_safe(VectorRegister dst, VectorRegister src);

  void extract_v(Register dst, VectorRegister src, BasicType bt, int idx, VectorRegister tmp);
  void extract_fp_v(FloatRegister dst, VectorRegister src, BasicType bt, int idx, VectorRegister tmp);

#endif // CPU_RISCV_C2_MACROASSEMBLER_RISCV_HPP
