//===-- W65816MCCodeEmitter.cpp - Convert W65816 code to machine code -----===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// 65816 instructions are 1-4 bytes: an 8-bit opcode followed by 0-3 bytes of
// operand data (immediate / direct-page / absolute / long / relative).  The
// tablegen-generated `getBinaryCodeForInstr` packs the full instruction into
// the low Size*8 bits of a uint64_t in little-endian order; we just copy
// that many bytes out.
//
// Each operand class carries an `EncoderMethod` naming one of the
// `encode*` helpers below.  Those helpers return the immediate value for
// literal operands and emit a relocation fixup for symbolic operands,
// tracking the per-instruction byte offset so block-move and other
// multi-operand instructions get correctly-positioned fixups.
//
//===----------------------------------------------------------------------===//

#include "MCTargetDesc/W65816FixupKinds.h"
#include "MCTargetDesc/W65816MCTargetDesc.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/MC/MCCodeEmitter.h"
#include "llvm/MC/MCContext.h"
#include "llvm/MC/MCExpr.h"
#include "llvm/MC/MCFixup.h"
#include "llvm/MC/MCInst.h"
#include "llvm/MC/MCInstrInfo.h"
#include "llvm/MC/MCRegisterInfo.h"
#include "llvm/MC/MCSubtargetInfo.h"
#include "llvm/Support/EndianStream.h"
#include "llvm/Support/ErrorHandling.h"

#define DEBUG_TYPE "mccodeemitter"

namespace llvm {

class W65816MCCodeEmitter : public MCCodeEmitter {
  MCContext &Ctx;
  const MCInstrInfo &MCII;

  // Byte position within the current instruction for the next operand's
  // fixup.  Reset to 1 at the start of each encodeInstruction call (byte 0
  // is always the opcode).
  mutable unsigned OperandOffset = 1;

  // Tablegen-provided.
  uint64_t getBinaryCodeForInstr(const MCInst &MI,
                                 SmallVectorImpl<MCFixup> &Fixups,
                                 const MCSubtargetInfo &STI) const;

  // Fallback operand encoder used when a custom EncoderMethod is not set.
  unsigned getMachineOpValue(const MCInst &MI, const MCOperand &MO,
                             SmallVectorImpl<MCFixup> &Fixups,
                             const MCSubtargetInfo &STI) const;

  // Per-operand encoders.  Each pulls one MCOperand from the instruction,
  // returns its literal bits for immediate operands, or emits a fixup of
  // the appropriate kind (and returns 0) for expression operands.
  unsigned encodeImm8(const MCInst &MI, unsigned OpIdx,
                      SmallVectorImpl<MCFixup> &Fixups,
                      const MCSubtargetInfo &STI) const;
  unsigned encodeImm16(const MCInst &MI, unsigned OpIdx,
                       SmallVectorImpl<MCFixup> &Fixups,
                       const MCSubtargetInfo &STI) const;
  unsigned encodeImm16Bank(const MCInst &MI, unsigned OpIdx,
                           SmallVectorImpl<MCFixup> &Fixups,
                           const MCSubtargetInfo &STI) const;
  unsigned encodeAddr8(const MCInst &MI, unsigned OpIdx,
                       SmallVectorImpl<MCFixup> &Fixups,
                       const MCSubtargetInfo &STI) const;
  unsigned encodeAddr16(const MCInst &MI, unsigned OpIdx,
                        SmallVectorImpl<MCFixup> &Fixups,
                        const MCSubtargetInfo &STI) const;
  unsigned encodeAddr24(const MCInst &MI, unsigned OpIdx,
                        SmallVectorImpl<MCFixup> &Fixups,
                        const MCSubtargetInfo &STI) const;
  unsigned encodePCRel8(const MCInst &MI, unsigned OpIdx,
                        SmallVectorImpl<MCFixup> &Fixups,
                        const MCSubtargetInfo &STI) const;
  unsigned encodePCRel16(const MCInst &MI, unsigned OpIdx,
                         SmallVectorImpl<MCFixup> &Fixups,
                         const MCSubtargetInfo &STI) const;

  // Shared implementation for every encode* helper.  `width` is the number
  // of bytes of the operand as it appears in the instruction stream; it
  // both advances the per-instruction offset and selects the fixup kind.
  unsigned encodeOperand(const MCInst &MI, unsigned OpIdx, unsigned width,
                         W65816::Fixups fixupKind,
                         SmallVectorImpl<MCFixup> &Fixups) const;

public:
  W65816MCCodeEmitter(MCContext &Ctx, const MCInstrInfo &MCII)
      : Ctx(Ctx), MCII(MCII) {}

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

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

  // First operand byte lives right after the opcode.
  OperandOffset = 1;
  uint64_t Bits = getBinaryCodeForInstr(MI, Fixups, STI);

  for (unsigned i = 0; i < Size; ++i) {
    CB.push_back(static_cast<char>((Bits >> (8 * i)) & 0xff));
  }
}

unsigned
W65816MCCodeEmitter::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()) {
    return static_cast<unsigned>(MO.getImm());
  }
  // All symbolic operands should be routed through the per-operand-class
  // EncoderMethods below.  Reaching this path means an operand was defined
  // without a custom encoder.
  llvm_unreachable("W65816: symbolic operand lacks EncoderMethod");
}

unsigned W65816MCCodeEmitter::encodeOperand(const MCInst &MI, unsigned OpIdx,
                                            unsigned width,
                                            W65816::Fixups fixupKind,
                                            SmallVectorImpl<MCFixup> &Fixups) const {
  const MCOperand &MO = MI.getOperand(OpIdx);
  unsigned offsetForThisOperand = OperandOffset;
  OperandOffset += width;

  if (MO.isImm()) {
    return static_cast<unsigned>(MO.getImm());
  }

  assert(MO.isExpr() && "unexpected operand kind for encoded operand");
  Fixups.push_back(MCFixup::create(offsetForThisOperand, MO.getExpr(),
                                   MCFixupKind(fixupKind)));
  return 0;
}

unsigned W65816MCCodeEmitter::encodeImm8(const MCInst &MI, unsigned OpIdx,
                                         SmallVectorImpl<MCFixup> &Fixups,
                                         const MCSubtargetInfo &STI) const {
  return encodeOperand(MI, OpIdx, 1, W65816::fixup_8, Fixups);
}

unsigned W65816MCCodeEmitter::encodeImm16(const MCInst &MI, unsigned OpIdx,
                                          SmallVectorImpl<MCFixup> &Fixups,
                                          const MCSubtargetInfo &STI) const {
  return encodeOperand(MI, OpIdx, 2, W65816::fixup_16, Fixups);
}

unsigned W65816MCCodeEmitter::encodeImm16Bank(const MCInst &MI, unsigned OpIdx,
                                              SmallVectorImpl<MCFixup> &Fixups,
                                              const MCSubtargetInfo &STI) const {
  return encodeOperand(MI, OpIdx, 2, W65816::fixup_bank16, Fixups);
}

unsigned W65816MCCodeEmitter::encodeAddr8(const MCInst &MI, unsigned OpIdx,
                                          SmallVectorImpl<MCFixup> &Fixups,
                                          const MCSubtargetInfo &STI) const {
  return encodeOperand(MI, OpIdx, 1, W65816::fixup_8, Fixups);
}

unsigned W65816MCCodeEmitter::encodeAddr16(const MCInst &MI, unsigned OpIdx,
                                           SmallVectorImpl<MCFixup> &Fixups,
                                           const MCSubtargetInfo &STI) const {
  return encodeOperand(MI, OpIdx, 2, W65816::fixup_16, Fixups);
}

unsigned W65816MCCodeEmitter::encodeAddr24(const MCInst &MI, unsigned OpIdx,
                                           SmallVectorImpl<MCFixup> &Fixups,
                                           const MCSubtargetInfo &STI) const {
  return encodeOperand(MI, OpIdx, 3, W65816::fixup_24, Fixups);
}

unsigned W65816MCCodeEmitter::encodePCRel8(const MCInst &MI, unsigned OpIdx,
                                           SmallVectorImpl<MCFixup> &Fixups,
                                           const MCSubtargetInfo &STI) const {
  return encodeOperand(MI, OpIdx, 1, W65816::fixup_8_pcrel, Fixups);
}

unsigned W65816MCCodeEmitter::encodePCRel16(const MCInst &MI, unsigned OpIdx,
                                            SmallVectorImpl<MCFixup> &Fixups,
                                            const MCSubtargetInfo &STI) const {
  return encodeOperand(MI, OpIdx, 2, W65816::fixup_16_pcrel, Fixups);
}

MCCodeEmitter *createW65816MCCodeEmitter(const MCInstrInfo &MCII,
                                         MCContext &Ctx) {
  return new W65816MCCodeEmitter(Ctx, MCII);
}

#include "W65816GenMCCodeEmitter.inc"

} // namespace llvm
