//===- VFABIDemangler.h - Vector Function ABI demangler ------- -*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file defines the VFABI demangling utility.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_IR_VFABIDEMANGLER_H
#define LLVM_IR_VFABIDEMANGLER_H

#include "llvm/ADT/SmallVector.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/Instructions.h"
#include "llvm/Support/Alignment.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/TypeSize.h"

namespace llvm {

/// Describes the type of Parameters
enum class VFParamKind {
  Vector,            // No semantic information.
  OMP_Linear,        // declare simd linear(i)
  OMP_LinearRef,     // declare simd linear(ref(i))
  OMP_LinearVal,     // declare simd linear(val(i))
  OMP_LinearUVal,    // declare simd linear(uval(i))
  OMP_LinearPos,     // declare simd linear(i:c) uniform(c)
  OMP_LinearValPos,  // declare simd linear(val(i:c)) uniform(c)
  OMP_LinearRefPos,  // declare simd linear(ref(i:c)) uniform(c)
  OMP_LinearUValPos, // declare simd linear(uval(i:c)) uniform(c)
  OMP_Uniform,       // declare simd uniform(i)
  GlobalPredicate,   // Global logical predicate that acts on all lanes
                     // of the input and output mask concurrently. For
                     // example, it is implied by the `M` token in the
                     // Vector Function ABI mangled name.
  Unknown
};

/// Describes the type of Instruction Set Architecture
enum class VFISAKind {
  AdvancedSIMD, // AArch64 Advanced SIMD (NEON)
  SVE,          // AArch64 Scalable Vector Extension
  RVV,          // RISC-V Vector Extension
  SSE,          // x86 SSE
  AVX,          // x86 AVX
  AVX2,         // x86 AVX2
  AVX512,       // x86 AVX512
  LLVM,         // LLVM internal ISA for functions that are not
  // attached to an existing ABI via name mangling.
  Unknown // Unknown ISA
};

/// Encapsulates information needed to describe a parameter.
///
/// The description of the parameter is not linked directly to
/// OpenMP or any other vector function description. This structure
/// is extendible to handle other paradigms that describe vector
/// functions and their parameters.
struct VFParameter {
  unsigned ParamPos;         // Parameter Position in Scalar Function.
  VFParamKind ParamKind;     // Kind of Parameter.
  int LinearStepOrPos = 0;   // Step or Position of the Parameter.
  Align Alignment = Align(); // Optional alignment in bytes, defaulted to 1.

  // Comparison operator.
  bool operator==(const VFParameter &Other) const {
    return std::tie(ParamPos, ParamKind, LinearStepOrPos, Alignment) ==
           std::tie(Other.ParamPos, Other.ParamKind, Other.LinearStepOrPos,
                    Other.Alignment);
  }
};

/// Contains the information about the kind of vectorization
/// available.
///
/// This object in independent on the paradigm used to
/// represent vector functions. in particular, it is not attached to
/// any target-specific ABI.
struct VFShape {
  ElementCount VF;                        // Vectorization factor.
  SmallVector<VFParameter, 8> Parameters; // List of parameter information.
  // Comparison operator.
  bool operator==(const VFShape &Other) const {
    return std::tie(VF, Parameters) == std::tie(Other.VF, Other.Parameters);
  }

  /// Update the parameter in position P.ParamPos to P.
  void updateParam(VFParameter P) {
    assert(P.ParamPos < Parameters.size() && "Invalid parameter position.");
    Parameters[P.ParamPos] = P;
    assert(hasValidParameterList() && "Invalid parameter list");
  }

  /// Retrieve the VFShape that can be used to map a scalar function to itself,
  /// with VF = 1.
  static VFShape getScalarShape(const FunctionType *FTy) {
    return VFShape::get(FTy, ElementCount::getFixed(1),
                        /*HasGlobalPredicate*/ false);
  }

  /// Retrieve the basic vectorization shape of the function, where all
  /// parameters are mapped to VFParamKind::Vector with \p EC lanes. Specifies
  /// whether the function has a Global Predicate argument via \p HasGlobalPred.
  static VFShape get(const FunctionType *FTy, ElementCount EC,
                     bool HasGlobalPred) {
    SmallVector<VFParameter, 8> Parameters;
    for (unsigned I = 0; I < FTy->getNumParams(); ++I)
      Parameters.push_back(VFParameter({I, VFParamKind::Vector}));
    if (HasGlobalPred)
      Parameters.push_back(
          VFParameter({FTy->getNumParams(), VFParamKind::GlobalPredicate}));

    return {EC, Parameters};
  }
  /// Validation check on the Parameters in the VFShape.
  LLVM_ABI bool hasValidParameterList() const;
};

/// Holds the VFShape for a specific scalar to vector function mapping.
struct VFInfo {
  VFShape Shape;          /// Classification of the vector function.
  std::string ScalarName; /// Scalar Function Name.
  std::string VectorName; /// Vector Function Name associated to this VFInfo.
  VFISAKind ISA;          /// Instruction Set Architecture.

  /// Returns the index of the first parameter with the kind 'GlobalPredicate',
  /// if any exist.
  std::optional<unsigned> getParamIndexForOptionalMask() const {
    unsigned ParamCount = Shape.Parameters.size();
    for (unsigned i = 0; i < ParamCount; ++i)
      if (Shape.Parameters[i].ParamKind == VFParamKind::GlobalPredicate)
        return i;

    return std::nullopt;
  }

  /// Returns true if at least one of the operands to the vectorized function
  /// has the kind 'GlobalPredicate'.
  bool isMasked() const { return getParamIndexForOptionalMask().has_value(); }
};

namespace VFABI {
/// LLVM Internal VFABI ISA token for vector functions.
static constexpr char const *_LLVM_ = "_LLVM_";
/// Prefix for internal name redirection for vector function that
/// tells the compiler to scalarize the call using the scalar name
/// of the function. For example, a mangled name like
/// `_ZGV_LLVM_N2v_foo(_LLVM_Scalarize_foo)` would tell the
/// vectorizer to vectorize the scalar call `foo`, and to scalarize
/// it once vectorization is done.
static constexpr char const *_LLVM_Scalarize_ = "_LLVM_Scalarize_";

/// Function to construct a VFInfo out of a mangled names in the
/// following format:
///
/// <VFABI_name>{(<redirection>)}
///
/// where <VFABI_name> is the name of the vector function, mangled according
/// to the rules described in the Vector Function ABI of the target vector
/// extension (or <isa> from now on). The <VFABI_name> is in the following
/// format:
///
/// _ZGV<isa><mask><vlen><parameters>_<scalarname>[(<redirection>)]
///
/// This methods support demangling rules for the following <isa>:
///
/// * AArch64: https://developer.arm.com/docs/101129/latest
///
/// * x86 (libmvec): https://sourceware.org/glibc/wiki/libmvec and
///  https://sourceware.org/glibc/wiki/libmvec?action=AttachFile&do=view&target=VectorABI.txt
///
/// \param MangledName -> input string in the format
/// _ZGV<isa><mask><vlen><parameters>_<scalarname>[(<redirection>)].
/// \param FTy -> FunctionType of the scalar function which we're trying to find
/// a vectorized variant for. This is required to determine the vectorization
/// factor for scalable vectors, since the mangled name doesn't encode that;
/// it needs to be derived from the widest element types of vector arguments
/// or return values.
LLVM_ABI std::optional<VFInfo> tryDemangleForVFABI(StringRef MangledName,
                                                   const FunctionType *FTy);

/// Retrieve the `VFParamKind` from a string token.
LLVM_ABI VFParamKind getVFParamKindFromString(const StringRef Token);

// Name of the attribute where the variant mappings are stored.
static constexpr char const *MappingsAttrName = "vector-function-abi-variant";

/// Populates a set of strings representing the Vector Function ABI variants
/// associated to the CallInst CI. If the CI does not contain the
/// vector-function-abi-variant attribute, we return without populating
/// VariantMappings, i.e. callers of getVectorVariantNames need not check for
/// the presence of the attribute (see InjectTLIMappings).
LLVM_ABI void
getVectorVariantNames(const CallInst &CI,
                      SmallVectorImpl<std::string> &VariantMappings);

/// Constructs a FunctionType by applying vector function information to the
/// type of a matching scalar function.
/// \param Info gets the vectorization factor (VF) and the VFParamKind of the
/// parameters.
/// \param ScalarFTy gets the Type information of parameters, as it is not
/// stored in \p Info.
/// \returns a pointer to a newly created vector FunctionType
LLVM_ABI FunctionType *createFunctionType(const VFInfo &Info,
                                          const FunctionType *ScalarFTy);

/// Overwrite the Vector Function ABI variants attribute with the names provide
/// in \p VariantMappings.
LLVM_ABI void setVectorVariantNames(CallInst *CI,
                                    ArrayRef<std::string> VariantMappings);

} // end namespace VFABI

} // namespace llvm

#endif // LLVM_IR_VFABIDEMANGLER_H
