//===-- W65816ELFObjectWriter.cpp - W65816 ELF Writer ---------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// Skeleton ELF object writer.  Relocation types will be assigned once the
// W65816 ELF ABI is finalised.
//
//===----------------------------------------------------------------------===//

#include "MCTargetDesc/W65816FixupKinds.h"
#include "MCTargetDesc/W65816MCTargetDesc.h"

#include "llvm/BinaryFormat/ELF.h"
#include "llvm/MC/MCELFObjectWriter.h"
#include "llvm/MC/MCFixup.h"
#include "llvm/MC/MCObjectWriter.h"
#include "llvm/MC/MCValue.h"
#include "llvm/Support/ErrorHandling.h"

using namespace llvm;

namespace {

class W65816ELFObjectWriter : public MCELFObjectTargetWriter {
public:
  // EM_NONE is a placeholder -- the real EM_ value for 65816 will be supplied
  // once the llvm-mos ELF specification is extended for the W65816 target.
  explicit W65816ELFObjectWriter(uint8_t OSABI)
      : MCELFObjectTargetWriter(/*Is64Bit=*/false, OSABI, ELF::EM_NONE,
                                /*HasRelocationAddend=*/true) {}

  ~W65816ELFObjectWriter() override = default;

protected:
  unsigned getRelocType(const MCFixup &Fixup, const MCValue &,
                        bool IsPCRel) const override {
    // Placeholder relocation numbers.  We are using EM_NONE so the full
    // (EM_, R_*) pair is unique; once a real EM_ value is assigned for the
    // W65816 target (see SESSION_STATE.md open question on ELF EM_), swap
    // these for the canonical R_W65816_* names.
    //
    // Generic FK_Data_* fixups are also accepted — the asm parser creates
    // them for things like `.word foo` and the JMP/JML address operand
    // when no target-specific fixup kind is hinted.  Map them to the
    // matching size-based reloc; PC-relative variants pick the *_pcrel
    // forms.  Without this, every hand-written .s reference to an extern
    // symbol came through `getRelocType` as a default-value (UB) reloc
    // type — observed as type 249 — and broke link816.py.
    auto Kind = Fixup.getKind();
    switch (Kind) {
    case W65816::fixup_8:        return 1;  // R_W65816_IMM8
    case W65816::fixup_16:       return 2;  // R_W65816_IMM16
    case W65816::fixup_24:       return 3;  // R_W65816_IMM24
    case W65816::fixup_8_pcrel:  return 4;  // R_W65816_PCREL8
    case W65816::fixup_16_pcrel: return 5;  // R_W65816_PCREL16
    case W65816::fixup_bank16:   return 6;  // R_W65816_BANK16
    case FK_Data_1:              return IsPCRel ? 4 : 1;
    case FK_Data_2:              return IsPCRel ? 5 : 2;
    case FK_Data_4:              return 3;  // truncated to IMM24 (we have
                                            // no 32-bit reloc); .long is
                                            // unusual on a 16-bit target.
    default:
      llvm_unreachable("W65816: unknown fixup kind");
    }
  }
};

} // end anonymous namespace

std::unique_ptr<MCObjectTargetWriter>
llvm::createW65816ELFObjectWriter(uint8_t OSABI) {
  return std::make_unique<W65816ELFObjectWriter>(OSABI);
}
