//===-- XtensaMCCodeEmitter.cpp - Convert Xtensa Code to Machine Code -----===//
//
//                     The LLVM Compiler Infrastructure
//
// 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 implements the XtensaMCCodeEmitter class.
//
//===----------------------------------------------------------------------===//

#include "MCTargetDesc/XtensaFixupKinds.h"
#include "MCTargetDesc/XtensaMCExpr.h"
#include "MCTargetDesc/XtensaMCTargetDesc.h"
#include "llvm/MC/MCCodeEmitter.h"
#include "llvm/MC/MCContext.h"
#include "llvm/MC/MCExpr.h"
#include "llvm/MC/MCInst.h"
#include "llvm/MC/MCInstrInfo.h"
#include "llvm/MC/MCRegisterInfo.h"

#define GET_INSTRMAP_INFO
#include "XtensaGenInstrInfo.inc"
#undef GET_INSTRMAP_INFO

using namespace llvm;

#define DEBUG_TYPE "mccodeemitter"

namespace {
class XtensaMCCodeEmitter : public MCCodeEmitter {
  const MCInstrInfo &MCII;
  MCContext &Ctx;
  bool IsLittleEndian;

public:
  XtensaMCCodeEmitter(const MCInstrInfo &mcii, MCContext &ctx, bool isLE)
      : MCII(mcii), Ctx(ctx), IsLittleEndian(isLE) {}

  ~XtensaMCCodeEmitter() {}

  // OVerride MCCodeEmitter.
  void encodeInstruction(const MCInst &MI, SmallVectorImpl<char> &CB,
                         SmallVectorImpl<MCFixup> &Fixups,
                         const MCSubtargetInfo &STI) const override;

private:
  // Automatically generated by TableGen.
  uint64_t getBinaryCodeForInstr(const MCInst &MI,
                                 SmallVectorImpl<MCFixup> &Fixups,
                                 const MCSubtargetInfo &STI) const;

  // Called by the TableGen code to get the binary encoding of operand
  // MO in MI.  Fixups is the list of fixups against MI.
  uint32_t getMachineOpValue(const MCInst &MI, const MCOperand &MO,
                             SmallVectorImpl<MCFixup> &Fixups,
                             const MCSubtargetInfo &STI) const;

  uint32_t getJumpTargetEncoding(const MCInst &MI, unsigned int OpNum,
                                 SmallVectorImpl<MCFixup> &Fixups,
                                 const MCSubtargetInfo &STI) const;

  uint32_t getBranchTargetEncoding(const MCInst &MI, unsigned int OpNum,
                                   SmallVectorImpl<MCFixup> &Fixups,
                                   const MCSubtargetInfo &STI) const;

  uint32_t getCallEncoding(const MCInst &MI, unsigned int OpNum,
                           SmallVectorImpl<MCFixup> &Fixups,
                           const MCSubtargetInfo &STI) const;

  uint32_t getL32RTargetEncoding(const MCInst &MI, unsigned OpNum,
                                 SmallVectorImpl<MCFixup> &Fixups,
                                 const MCSubtargetInfo &STI) const;

  uint32_t getMemRegEncoding(const MCInst &MI, unsigned OpNo,
                             SmallVectorImpl<MCFixup> &Fixups,
                             const MCSubtargetInfo &STI) const;

  uint32_t getImm8OpValue(const MCInst &MI, unsigned OpNo,
                          SmallVectorImpl<MCFixup> &Fixups,
                          const MCSubtargetInfo &STI) const;

  uint32_t getImm8_sh8OpValue(const MCInst &MI, unsigned OpNo,
                              SmallVectorImpl<MCFixup> &Fixups,
                              const MCSubtargetInfo &STI) const;

  uint32_t getImm12OpValue(const MCInst &MI, unsigned OpNo,
                           SmallVectorImpl<MCFixup> &Fixups,
                           const MCSubtargetInfo &STI) const;

  uint32_t getUimm4OpValue(const MCInst &MI, unsigned OpNo,
                           SmallVectorImpl<MCFixup> &Fixups,
                           const MCSubtargetInfo &STI) const;

  uint32_t getUimm5OpValue(const MCInst &MI, unsigned OpNo,
                           SmallVectorImpl<MCFixup> &Fixups,
                           const MCSubtargetInfo &STI) const;

  uint32_t getImm1_16OpValue(const MCInst &MI, unsigned OpNo,
                             SmallVectorImpl<MCFixup> &Fixups,
                             const MCSubtargetInfo &STI) const;

  uint32_t getShimm1_31OpValue(const MCInst &MI, unsigned OpNo,
                               SmallVectorImpl<MCFixup> &Fixups,
                               const MCSubtargetInfo &STI) const;

  uint32_t getB4constOpValue(const MCInst &MI, unsigned OpNo,
                             SmallVectorImpl<MCFixup> &Fixups,
                             const MCSubtargetInfo &STI) const;

  uint32_t getB4constuOpValue(const MCInst &MI, unsigned OpNo,
                              SmallVectorImpl<MCFixup> &Fixups,
                              const MCSubtargetInfo &STI) const;
};
} // namespace

MCCodeEmitter *llvm::createXtensaMCCodeEmitter(const MCInstrInfo &MCII,
                                               MCContext &Ctx) {
  return new XtensaMCCodeEmitter(MCII, Ctx, true);
}

void XtensaMCCodeEmitter::encodeInstruction(const MCInst &MI,
                                            SmallVectorImpl<char> &CB,
                                            SmallVectorImpl<MCFixup> &Fixups,
                                            const MCSubtargetInfo &STI) const {
  uint64_t Bits = getBinaryCodeForInstr(MI, Fixups, STI);
  unsigned Size = MCII.get(MI.getOpcode()).getSize();

  if (IsLittleEndian) {
    // Little-endian insertion of Size bytes.
    unsigned ShiftValue = 0;
    for (unsigned I = 0; I != Size; ++I) {
      CB.push_back(char(Bits >> ShiftValue));
      ShiftValue += 8;
    }
  } else {
    // TODO Big-endian insertion of Size bytes.
    report_fatal_error("Big-endian mode currently is not supported!");
  }
}

uint32_t
XtensaMCCodeEmitter::getMachineOpValue(const MCInst &MI, const MCOperand &MO,
                                       SmallVectorImpl<MCFixup> &Fixups,
                                       const MCSubtargetInfo &STI) const {
  if (MO.isReg())
    return Ctx.getRegisterInfo()->getEncodingValue(MO.getReg());
  if (MO.isImm()) {
    uint32_t Res = static_cast<uint32_t>(MO.getImm());
    return Res;
  }

  report_fatal_error("Unhandled expression!");
  return 0;
}

uint32_t
XtensaMCCodeEmitter::getJumpTargetEncoding(const MCInst &MI, unsigned int OpNum,
                                           SmallVectorImpl<MCFixup> &Fixups,
                                           const MCSubtargetInfo &STI) const {
  const MCOperand &MO = MI.getOperand(OpNum);

  if (MO.isImm())
    return MO.getImm();

  const MCExpr *Expr = MO.getExpr();
  Fixups.push_back(MCFixup::create(
      0, Expr, MCFixupKind(Xtensa::fixup_xtensa_jump_18), MI.getLoc()));
  return 0;
}

uint32_t XtensaMCCodeEmitter::getBranchTargetEncoding(
    const MCInst &MI, unsigned int OpNum, SmallVectorImpl<MCFixup> &Fixups,
    const MCSubtargetInfo &STI) const {
  const MCOperand &MO = MI.getOperand(OpNum);
  if (MO.isImm())
    return static_cast<uint32_t>(MO.getImm());

  const MCExpr *Expr = MO.getExpr();
  switch (MI.getOpcode()) {
  case Xtensa::BEQZ:
  case Xtensa::BGEZ:
  case Xtensa::BLTZ:
  case Xtensa::BNEZ:
    Fixups.push_back(MCFixup::create(
        0, Expr, MCFixupKind(Xtensa::fixup_xtensa_branch_12), MI.getLoc()));
    return 0;
  default:
    Fixups.push_back(MCFixup::create(
        0, Expr, MCFixupKind(Xtensa::fixup_xtensa_branch_8), MI.getLoc()));
    return 0;
  }
}

uint32_t
XtensaMCCodeEmitter::getCallEncoding(const MCInst &MI, unsigned int OpNum,
                                     SmallVectorImpl<MCFixup> &Fixups,
                                     const MCSubtargetInfo &STI) const {
  const MCOperand &MO = MI.getOperand(OpNum);
  if (MO.isImm()) {
    int32_t Res = MO.getImm();
    if (Res & 0x3) {
      llvm_unreachable("Unexpected operand value!");
    }
    Res >>= 2;
    return Res;
  }

  assert((MO.isExpr()) && "Unexpected operand value!");
  const MCExpr *Expr = MO.getExpr();
  Fixups.push_back(MCFixup::create(
      0, Expr, MCFixupKind(Xtensa::fixup_xtensa_call_18), MI.getLoc()));
  return 0;
}

uint32_t
XtensaMCCodeEmitter::getL32RTargetEncoding(const MCInst &MI, unsigned OpNum,
                                           SmallVectorImpl<MCFixup> &Fixups,
                                           const MCSubtargetInfo &STI) const {
  const MCOperand &MO = MI.getOperand(OpNum);
  if (MO.isImm()) {
    int32_t Res = MO.getImm();
    // We don't check first 2 bits, because in these bits we could store first 2
    // bits of instruction address
    Res >>= 2;
    return Res;
  }

  assert((MO.isExpr()) && "Unexpected operand value!");

  Fixups.push_back(MCFixup::create(
      0, MO.getExpr(), MCFixupKind(Xtensa::fixup_xtensa_l32r_16), MI.getLoc()));
  return 0;
}

uint32_t
XtensaMCCodeEmitter::getMemRegEncoding(const MCInst &MI, unsigned OpNo,
                                       SmallVectorImpl<MCFixup> &Fixups,
                                       const MCSubtargetInfo &STI) const {
  assert(MI.getOperand(OpNo + 1).isImm());

  uint32_t Res = static_cast<uint32_t>(MI.getOperand(OpNo + 1).getImm());

  switch (MI.getOpcode()) {
  case Xtensa::S16I:
  case Xtensa::L16SI:
  case Xtensa::L16UI:
    if (Res & 0x1) {
      report_fatal_error("Unexpected operand value!");
    }
    Res >>= 1;
    break;
  case Xtensa::S32I:
  case Xtensa::L32I:
    if (Res & 0x3) {
      report_fatal_error("Unexpected operand value!");
    }
    Res >>= 2;
    break;
  }
  
  assert((isUInt<8>(Res)) && "Unexpected operand value!");

  uint32_t OffBits = Res << 4;
  uint32_t RegBits = getMachineOpValue(MI, MI.getOperand(OpNo), Fixups, STI);

  return ((OffBits & 0xFF0) | RegBits);
}

uint32_t XtensaMCCodeEmitter::getImm8OpValue(const MCInst &MI, unsigned OpNo,
                                             SmallVectorImpl<MCFixup> &Fixups,
                                             const MCSubtargetInfo &STI) const {
  const MCOperand &MO = MI.getOperand(OpNo);
  int32_t Res = MO.getImm();

  assert(((Res >= -128) && (Res <= 127)) && "Unexpected operand value!");

  return (Res & 0xff);
}

uint32_t
XtensaMCCodeEmitter::getImm8_sh8OpValue(const MCInst &MI, unsigned OpNo,
                                        SmallVectorImpl<MCFixup> &Fixups,
                                        const MCSubtargetInfo &STI) const {
  const MCOperand &MO = MI.getOperand(OpNo);
  int32_t Res = MO.getImm();

  assert(((Res >= -32768) && (Res <= 32512) && ((Res & 0xff) == 0)) &&
         "Unexpected operand value!");

  return (Res & 0xffff);
}

uint32_t
XtensaMCCodeEmitter::getImm12OpValue(const MCInst &MI, unsigned OpNo,
                                     SmallVectorImpl<MCFixup> &Fixups,
                                     const MCSubtargetInfo &STI) const {
  const MCOperand &MO = MI.getOperand(OpNo);
  int32_t Res = MO.getImm();

  assert(((Res >= -2048) && (Res <= 2047)) && "Unexpected operand value!");

  return (Res & 0xfff);
}

uint32_t
XtensaMCCodeEmitter::getUimm4OpValue(const MCInst &MI, unsigned OpNo,
                                     SmallVectorImpl<MCFixup> &Fixups,
                                     const MCSubtargetInfo &STI) const {
  const MCOperand &MO = MI.getOperand(OpNo);
  uint32_t Res = static_cast<uint32_t>(MO.getImm());

  assert((Res <= 15) && "Unexpected operand value!");

  return Res & 0xf;
}

uint32_t
XtensaMCCodeEmitter::getUimm5OpValue(const MCInst &MI, unsigned OpNo,
                                     SmallVectorImpl<MCFixup> &Fixups,
                                     const MCSubtargetInfo &STI) const {
  const MCOperand &MO = MI.getOperand(OpNo);
  uint32_t Res = static_cast<uint32_t>(MO.getImm());

  assert((Res <= 31) && "Unexpected operand value!");

  return (Res & 0x1f);
}

uint32_t
XtensaMCCodeEmitter::getShimm1_31OpValue(const MCInst &MI, unsigned OpNo,
                                         SmallVectorImpl<MCFixup> &Fixups,
                                         const MCSubtargetInfo &STI) const {
  const MCOperand &MO = MI.getOperand(OpNo);
  uint32_t Res = static_cast<uint32_t>(MO.getImm());

  assert(((Res >= 1) && (Res <= 31)) && "Unexpected operand value!");

  return ((32 - Res) & 0x1f);
}

uint32_t
XtensaMCCodeEmitter::getImm1_16OpValue(const MCInst &MI, unsigned OpNo,
                                       SmallVectorImpl<MCFixup> &Fixups,
                                       const MCSubtargetInfo &STI) const {
  const MCOperand &MO = MI.getOperand(OpNo);
  uint32_t Res = static_cast<uint32_t>(MO.getImm());

  assert(((Res >= 1) && (Res <= 16)) && "Unexpected operand value!");

  return (Res - 1);
}

uint32_t
XtensaMCCodeEmitter::getB4constOpValue(const MCInst &MI, unsigned OpNo,
                                       SmallVectorImpl<MCFixup> &Fixups,
                                       const MCSubtargetInfo &STI) const {
  const MCOperand &MO = MI.getOperand(OpNo);
  uint32_t Res = static_cast<uint32_t>(MO.getImm());

  switch (Res) {
  case 0xffffffff:
    Res = 0;
    break;
  case 1:
  case 2:
  case 3:
  case 4:
  case 5:
  case 6:
  case 7:
  case 8:
    break;
  case 10:
    Res = 9;
    break;
  case 12:
    Res = 10;
    break;
  case 16:
    Res = 11;
    break;
  case 32:
    Res = 12;
    break;
  case 64:
    Res = 13;
    break;
  case 128:
    Res = 14;
    break;
  case 256:
    Res = 15;
    break;
  default:
    llvm_unreachable("Unexpected operand value!");
  }

  return Res;
}

uint32_t
XtensaMCCodeEmitter::getB4constuOpValue(const MCInst &MI, unsigned OpNo,
                                        SmallVectorImpl<MCFixup> &Fixups,
                                        const MCSubtargetInfo &STI) const {
  const MCOperand &MO = MI.getOperand(OpNo);
  uint32_t Res = static_cast<uint32_t>(MO.getImm());

  switch (Res) {
  case 32768:
    Res = 0;
    break;
  case 65536:
    Res = 1;
    break;
  case 2:
  case 3:
  case 4:
  case 5:
  case 6:
  case 7:
  case 8:
    break;
  case 10:
    Res = 9;
    break;
  case 12:
    Res = 10;
    break;
  case 16:
    Res = 11;
    break;
  case 32:
    Res = 12;
    break;
  case 64:
    Res = 13;
    break;
  case 128:
    Res = 14;
    break;
  case 256:
    Res = 15;
    break;
  default:
    llvm_unreachable("Unexpected operand value!");
  }

  return Res;
}
#include "XtensaGenMCCodeEmitter.inc"
