/*
 * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
 * Copyright (c) 2014, 2020, Red Hat Inc. 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_AARCH64_VM_VERSION_AARCH64_HPP
#define CPU_AARCH64_VM_VERSION_AARCH64_HPP

#include "spin_wait_aarch64.hpp"
#include "runtime/abstract_vm_version.hpp"
#include "utilities/sizes.hpp"

class VM_Version : public Abstract_VM_Version {
  friend class VMStructs;
  friend class JVMCIVMStructs;

protected:
  static int _cpu;
  static int _model;
  static int _model2;
  static int _variant;
  static int _revision;
  static int _stepping;

  static int _zva_length;
  static int _dcache_line_size;
  static int _icache_line_size;
  static int _initial_sve_vector_length;
  static int _max_supported_sve_vector_length;
  static bool _rop_protection;
  static uintptr_t _pac_mask;

  static SpinWait _spin_wait;

  // Read additional info using OS-specific interfaces
  static void get_os_cpu_info();

  // Sets the SVE length and returns a new actual value or negative on error.
  // If the len is larger than the system largest supported SVE vector length,
  // the function sets the largest supported value.
  static int set_and_get_current_sve_vector_length(int len);
  static int get_current_sve_vector_length();

public:
  // Initialization
  static void initialize();
  static void check_virtualizations();

  static void print_platform_virtualization_info(outputStream*);

  // Asserts
  static void assert_is_initialized() {
  }

  static bool expensive_load(int ld_size, int scale) {
    if (cpu_family() == CPU_ARM) {
      // Half-word load with index shift by 1 (aka scale is 2) has
      // extra cycle latency, e.g. ldrsh w0, [x1,w2,sxtw #1].
      if (ld_size == 2 && scale == 2) {
        return true;
      }
    }
    return false;
  }

  // The CPU implementer codes can be found in
  // ARM Architecture Reference Manual ARMv8, for ARMv8-A architecture profile
  // https://developer.arm.com/docs/ddi0487/latest
  // Arm can assign codes that are not published in the manual.
  // Apple's code is defined in
  // https://github.com/apple/darwin-xnu/blob/33eb983/osfmk/arm/cpuid.h#L62
  enum Family {
    CPU_AMPERE    = 0xC0,
    CPU_ARM       = 'A',
    CPU_BROADCOM  = 'B',
    CPU_CAVIUM    = 'C',
    CPU_DEC       = 'D',
    CPU_HISILICON = 'H',
    CPU_INFINEON  = 'I',
    CPU_MOTOROLA  = 'M',
    CPU_NVIDIA    = 'N',
    CPU_AMCC      = 'P',
    CPU_QUALCOM   = 'Q',
    CPU_MARVELL   = 'V',
    CPU_INTEL     = 'i',
    CPU_APPLE     = 'a',
  };

enum Ampere_CPU_Model {
    CPU_MODEL_EMAG      = 0x0,   /* CPU implementer is CPU_AMCC */
    CPU_MODEL_ALTRA     = 0xd0c, /* CPU implementer is CPU_ARM, Neoverse N1 */
    CPU_MODEL_ALTRAMAX  = 0xd0c, /* CPU implementer is CPU_ARM, Neoverse N1 */
    CPU_MODEL_AMPERE_1  = 0xac3, /* CPU implementer is CPU_AMPERE */
    CPU_MODEL_AMPERE_1A = 0xac4, /* CPU implementer is CPU_AMPERE */
    CPU_MODEL_AMPERE_1B = 0xac5  /* AMPERE_1B core Implements ARMv8.7 with CSSC, MTE, SM3/SM4 extensions */
};

#define CPU_FEATURE_FLAGS(decl)               \
    decl(FP,            fp,            0)     \
    decl(ASIMD,         asimd,         1)     \
    decl(EVTSTRM,       evtstrm,       2)     \
    decl(AES,           aes,           3)     \
    decl(PMULL,         pmull,         4)     \
    decl(SHA1,          sha1,          5)     \
    decl(SHA2,          sha256,        6)     \
    decl(CRC32,         crc32,         7)     \
    decl(LSE,           lse,           8)     \
    decl(FPHP,          fphp,          9)     \
    decl(ASIMDHP,       asimdhp,       10)    \
    decl(DCPOP,         dcpop,         16)    \
    decl(SHA3,          sha3,          17)    \
    decl(SHA512,        sha512,        21)    \
    decl(SVE,           sve,           22)    \
    decl(PACA,          paca,          30)    \
    /* flags above must follow Linux HWCAP */ \
    decl(SVEBITPERM,    svebitperm,    27)    \
    decl(SVE2,          sve2,          28)    \
    decl(A53MAC,        a53mac,        31)

  enum Feature_Flag {
#define DECLARE_CPU_FEATURE_FLAG(id, name, bit) CPU_##id = (1 << bit),
    CPU_FEATURE_FLAGS(DECLARE_CPU_FEATURE_FLAG)
#undef DECLARE_CPU_FEATURE_FLAG
  };

  // Feature identification
#define CPU_FEATURE_DETECTION(id, name, bit) \
  static bool supports_##name() { return (_features & CPU_##id) != 0; };
  CPU_FEATURE_FLAGS(CPU_FEATURE_DETECTION)
#undef CPU_FEATURE_DETECTION

  static int cpu_family()                     { return _cpu; }
  static int cpu_model()                      { return _model; }
  static int cpu_model2()                     { return _model2; }
  static int cpu_variant()                    { return _variant; }
  static int cpu_revision()                   { return _revision; }

  static bool model_is(int cpu_model) {
    return _model == cpu_model || _model2 == cpu_model;
  }

  static bool is_zva_enabled() { return 0 <= _zva_length; }
  static int zva_length() {
    assert(is_zva_enabled(), "ZVA not available");
    return _zva_length;
  }

  static int icache_line_size() { return _icache_line_size; }
  static int dcache_line_size() { return _dcache_line_size; }
  static int get_initial_sve_vector_length()        { return _initial_sve_vector_length; };
  static int get_max_supported_sve_vector_length()  { return _max_supported_sve_vector_length; };

  // Aarch64 supports fast class initialization checks
  static bool supports_fast_class_init_checks() { return true; }
  constexpr static bool supports_stack_watermark_barrier() { return true; }
  constexpr static bool supports_recursive_lightweight_locking() { return true; }

  constexpr static bool supports_secondary_supers_table() { return true; }

  static void get_compatible_board(char *buf, int buflen);

  static const SpinWait& spin_wait_desc() { return _spin_wait; }

  static bool supports_on_spin_wait() { return _spin_wait.inst() != SpinWait::NONE; }

  static bool supports_float16() { return true; }

#ifdef __APPLE__
  // Is the CPU running emulated (for example macOS Rosetta running x86_64 code on M1 ARM (aarch64)
  static bool is_cpu_emulated();
#endif

  static void initialize_cpu_information(void);

  static bool use_rop_protection() { return _rop_protection; }

  // For common 64/128-bit unpredicated vector operations, we may prefer
  // emitting NEON instructions rather than the corresponding SVE instructions.
  static bool use_neon_for_vector(int vector_length_in_bytes) {
    return vector_length_in_bytes <= 16;
  }
};

#endif // CPU_AARCH64_VM_VERSION_AARCH64_HPP
