//===- subzero/src/IceIntrinsics.h - List of Ice Intrinsics -----*- C++ -*-===//
//
//                        The Subzero Code Generator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
/// \brief Declares the kinds of intrinsics supported by PNaCl.
///
//===----------------------------------------------------------------------===//

#ifndef SUBZERO_SRC_ICEINTRINSICS_H
#define SUBZERO_SRC_ICEINTRINSICS_H

#include "IceDefs.h"
#include "IceStringPool.h"
#include "IceTypes.h"

namespace Ice {

class InstIntrinsic;

static constexpr size_t kMaxIntrinsicParameters = 6;

namespace Intrinsics {

/// Some intrinsics allow overloading by type. This enum collapses all
/// overloads into a single ID, but the type can still be recovered by the
/// type of the intrinsic's return value and parameters.
enum IntrinsicID {
  UnknownIntrinsic = 0,
  // Arbitrary (alphabetical) order.
  AtomicCmpxchg,
  AtomicFence,
  AtomicFenceAll,
  AtomicIsLockFree,
  AtomicLoad,
  AtomicRMW,
  AtomicStore,
  Bswap,
  Ctlz,
  Ctpop,
  Cttz,
  Fabs,
  Longjmp,
  Memcpy,
  Memmove,
  Memset,
  NaClReadTP,
  Setjmp,
  Sqrt,
  Stacksave,
  Stackrestore,
  Trap,
  // The intrinsics below are not part of the PNaCl specification.
  AddSaturateSigned,
  AddSaturateUnsigned,
  LoadSubVector,
  MultiplyAddPairs,
  MultiplyHighSigned,
  MultiplyHighUnsigned,
  Nearbyint,
  Round,
  SignMask,
  StoreSubVector,
  SubtractSaturateSigned,
  SubtractSaturateUnsigned,
  VectorPackSigned,
  VectorPackUnsigned
};

/// Operations that can be represented by the AtomicRMW intrinsic.
///
/// Do not reorder these values: their order offers forward compatibility of
/// bitcode targeted to PNaCl.
enum AtomicRMWOperation {
  AtomicInvalid = 0, // Invalid, keep first.
  AtomicAdd,
  AtomicSub,
  AtomicOr,
  AtomicAnd,
  AtomicXor,
  AtomicExchange,
  AtomicNum // Invalid, keep last.
};

/// Memory orderings supported by PNaCl IR.
///
/// Do not reorder these values: their order offers forward compatibility of
/// bitcode targeted to PNaCl.
enum MemoryOrder {
  MemoryOrderInvalid = 0, // Invalid, keep first.
  MemoryOrderRelaxed,
  MemoryOrderConsume,
  MemoryOrderAcquire,
  MemoryOrderRelease,
  MemoryOrderAcquireRelease,
  MemoryOrderSequentiallyConsistent,
  MemoryOrderNum // Invalid, keep last.
};

/// Verify memory ordering rules for atomic intrinsics. For AtomicCmpxchg,
/// Order is the "success" ordering and OrderOther is the "failure" ordering.
/// Returns true if valid, false if invalid.
// TODO(stichnot,kschimpf): Perform memory order validation in the bitcode
// reader/parser, allowing LLVM and Subzero to share. See
// https://code.google.com/p/nativeclient/issues/detail?id=4126 .
bool isMemoryOrderValid(IntrinsicID ID, uint64_t Order,
                        uint64_t OrderOther = MemoryOrderInvalid);

enum SideEffects { SideEffects_F = 0, SideEffects_T = 1 };

enum ReturnsTwice { ReturnsTwice_F = 0, ReturnsTwice_T = 1 };

enum MemoryWrite { MemoryWrite_F = 0, MemoryWrite_T = 1 };

/// Basic attributes related to each intrinsic, that are relevant to code
/// generation.
struct IntrinsicInfo {
  enum IntrinsicID ID : 29;
  enum SideEffects HasSideEffects : 1;
  enum ReturnsTwice ReturnsTwice : 1;
  enum MemoryWrite IsMemoryWrite : 1;
};
static_assert(sizeof(IntrinsicInfo) == 4, "IntrinsicInfo should be 32 bits");

/// The types of validation values for FullIntrinsicInfo.validateIntrinsic.
enum ValidateIntrinsicValue {
  IsValidIntrinsic, /// Valid use of instrinsic.
  BadReturnType,    /// Return type invalid for intrinsic.
  WrongNumOfArgs,   /// Wrong number of arguments for intrinsic.
  WrongArgType,     /// Argument of wrong type.
};

/// The complete set of information about an intrinsic.
struct FullIntrinsicInfo {
  struct IntrinsicInfo Info; /// Information that CodeGen would care about.

  // Sanity check during parsing.
  Type Signature[kMaxIntrinsicParameters];
  uint8_t NumTypes;

  /// Validates that type signature matches intrinsic. If WrongArgumentType is
  /// returned, ArgIndex is set to corresponding argument index.
  ValidateIntrinsicValue validateIntrinsic(const Ice::InstIntrinsic *Intrinsic,
                                           SizeT &ArgIndex) const;

  /// Returns the return type of the intrinsic.
  Type getReturnType() const {
    assert(NumTypes > 0);
    return Signature[0];
  }

  /// Returns number of arguments expected.
  SizeT getNumArgs() const {
    assert(NumTypes > 0);
    return NumTypes - 1;
  }

  /// Returns type of Index-th argument.
  Type getArgType(SizeT Index) const;
};

} // namespace Intrinsics

} // end of namespace Ice

#endif // SUBZERO_SRC_ICEINTRINSICS_H
