//===-- LanaiAluCode.h - ALU operator encoding ----------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// The encoding for ALU operators used in RM and RRM operands
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_LIB_TARGET_LANAI_LANAIALUCODE_H
#define LLVM_LIB_TARGET_LANAI_LANAIALUCODE_H

#include "llvm/ADT/StringSwitch.h"
#include "llvm/CodeGen/ISDOpcodes.h"
#include "llvm/Support/ErrorHandling.h"

namespace llvm {
namespace LPAC {
enum AluCode {
  ADD = 0x00,
  ADDC = 0x01,
  SUB = 0x02,
  SUBB = 0x03,
  AND = 0x04,
  OR = 0x05,
  XOR = 0x06,
  SPECIAL = 0x07,

  // Shift instructions are treated as SPECIAL when encoding the machine
  // instruction, but kept distinct until lowering. The constant values are
  // chosen to ease lowering.
  SHL = 0x17,
  SRL = 0x27,
  SRA = 0x37,

  // Indicates an unknown/unsupported operator
  UNKNOWN = 0xFF,
};

// Bits indicating post- and pre-operators should be tested and set using Is*
// and Make* utility functions
const int Lanai_PRE_OP = 0x40;
const int Lanai_POST_OP = 0x80;

inline static unsigned encodeLanaiAluCode(unsigned AluOp) {
  unsigned const OP_ENCODING_MASK = 0x07;
  return AluOp & OP_ENCODING_MASK;
}

inline static unsigned getAluOp(unsigned AluOp) {
  unsigned const ALU_MASK = 0x3F;
  return AluOp & ALU_MASK;
}

inline static bool isPreOp(unsigned AluOp) { return AluOp & Lanai_PRE_OP; }

inline static bool isPostOp(unsigned AluOp) { return AluOp & Lanai_POST_OP; }

inline static unsigned makePreOp(unsigned AluOp) {
  assert(!isPostOp(AluOp) && "Operator can't be a post- and pre-op");
  return AluOp | Lanai_PRE_OP;
}

inline static unsigned makePostOp(unsigned AluOp) {
  assert(!isPreOp(AluOp) && "Operator can't be a post- and pre-op");
  return AluOp | Lanai_POST_OP;
}

inline static bool modifiesOp(unsigned AluOp) {
  return isPreOp(AluOp) | isPostOp(AluOp);
}

inline static const char *lanaiAluCodeToString(unsigned AluOp) {
  switch (getAluOp(AluOp)) {
  case ADD:
    return "add";
  case ADDC:
    return "addc";
  case SUB:
    return "sub";
  case SUBB:
    return "subb";
  case AND:
    return "and";
  case OR:
    return "or";
  case XOR:
    return "xor";
  case SHL:
    return "sh";
  case SRL:
    return "sh";
  case SRA:
    return "sha";
  default:
    llvm_unreachable("Invalid ALU code.");
  }
}

inline static AluCode stringToLanaiAluCode(StringRef S) {
  return StringSwitch<AluCode>(S)
      .Case("add", ADD)
      .Case("addc", ADDC)
      .Case("sub", SUB)
      .Case("subb", SUBB)
      .Case("and", AND)
      .Case("or", OR)
      .Case("xor", XOR)
      .Case("sh", SHL)
      .Case("srl", SRL)
      .Case("sha", SRA)
      .Default(UNKNOWN);
}

inline static AluCode isdToLanaiAluCode(ISD::NodeType Node_type) {
  switch (Node_type) {
  case ISD::ADD:
    return AluCode::ADD;
  case ISD::ADDE:
    return AluCode::ADDC;
  case ISD::SUB:
    return AluCode::SUB;
  case ISD::SUBE:
    return AluCode::SUBB;
  case ISD::AND:
    return AluCode::AND;
  case ISD::OR:
    return AluCode::OR;
  case ISD::XOR:
    return AluCode::XOR;
  case ISD::SHL:
    return AluCode::SHL;
  case ISD::SRL:
    return AluCode::SRL;
  case ISD::SRA:
    return AluCode::SRA;
  default:
    return AluCode::UNKNOWN;
  }
}
} // namespace LPAC
} // namespace llvm

#endif // LLVM_LIB_TARGET_LANAI_LANAIALUCODE_H
