Index: llvm/include/llvm/BinaryFormat/ELF.h =================================================================== --- llvm/include/llvm/BinaryFormat/ELF.h +++ llvm/include/llvm/BinaryFormat/ELF.h @@ -747,6 +747,11 @@ #include "ELFRelocs/BPF.def" }; +// ELF Relocation types for M68k +enum { +#include "ELFRelocs/M68k.def" +}; + // MSP430 specific e_flags enum : unsigned { EF_MSP430_MACH_MSP430x11 = 11, Index: llvm/include/llvm/BinaryFormat/ELFRelocs/M68k.def =================================================================== --- /dev/null +++ llvm/include/llvm/BinaryFormat/ELFRelocs/M68k.def @@ -0,0 +1,49 @@ +#ifndef ELF_RELOC +#error "ELF_RELOC must be defined" +#endif + +ELF_RELOC(R_68K_NONE, 0) /* No reloc */ +ELF_RELOC(R_68K_32, 1) /* Direct 32 bit */ +ELF_RELOC(R_68K_16, 2) /* Direct 16 bit */ +ELF_RELOC(R_68K_8, 3) /* Direct 8 bit */ +ELF_RELOC(R_68K_PC32, 4) /* PC relative 32 bit */ +ELF_RELOC(R_68K_PC16, 5) /* PC relative 16 bit */ +ELF_RELOC(R_68K_PC8, 6) /* PC relative 8 bit */ +ELF_RELOC(R_68K_GOTPCREL32, 7) /* 32 bit PC relative GOT entry */ +ELF_RELOC(R_68K_GOTPCREL16, 8) /* 16 bit PC relative GOT entry */ +ELF_RELOC(R_68K_GOTPCREL8, 9) /* 8 bit PC relative GOT entry */ +ELF_RELOC(R_68K_GOTOFF32, 10) /* 32 bit GOT offset */ +ELF_RELOC(R_68K_GOTOFF16, 11) /* 16 bit GOT offset */ +ELF_RELOC(R_68K_GOTOFF8, 12) /* 8 bit GOT offset */ +ELF_RELOC(R_68K_PLT32, 13) /* 32 bit PC relative PLT address */ +ELF_RELOC(R_68K_PLT16, 14) /* 16 bit PC relative PLT address */ +ELF_RELOC(R_68K_PLT8, 15) /* 8 bit PC relative PLT address */ +ELF_RELOC(R_68K_PLTOFF32, 16) /* 32 bit PLT offset */ +ELF_RELOC(R_68K_PLTOFF16, 17) /* 16 bit PLT offset */ +ELF_RELOC(R_68K_PLTOFF8, 18) /* 8 bit PLT offset */ +ELF_RELOC(R_68K_COPY, 19) /* Copy symbol at runtime */ +ELF_RELOC(R_68K_GLOB_DAT, 20) /* Create GOT entry */ +ELF_RELOC(R_68K_JMP_SLOT, 21) /* Create PLT entry */ +ELF_RELOC(R_68K_RELATIVE, 22) /* Adjust by program base */ +/* These are GNU extensions to enable C++ vtable garbage collection. */ +ELF_RELOC(R_68K_GNU_VTINHERIT, 23) +ELF_RELOC(R_68K_GNU_VTENTRY, 24) +/* TLS static relocations. */ +ELF_RELOC(R_68K_TLS_GD32, 25) +ELF_RELOC(R_68K_TLS_GD16, 26) +ELF_RELOC(R_68K_TLS_GD8, 27) +ELF_RELOC(R_68K_TLS_LDM32, 28) +ELF_RELOC(R_68K_TLS_LDM16, 29) +ELF_RELOC(R_68K_TLS_LDM8, 30) +ELF_RELOC(R_68K_TLS_LDO32, 31) +ELF_RELOC(R_68K_TLS_LDO16, 32) +ELF_RELOC(R_68K_TLS_LDO8, 33) +ELF_RELOC(R_68K_TLS_IE32, 34) +ELF_RELOC(R_68K_TLS_IE16, 35) +ELF_RELOC(R_68K_TLS_IE8, 36) +ELF_RELOC(R_68K_TLS_LE32, 37) +ELF_RELOC(R_68K_TLS_LE16, 38) +ELF_RELOC(R_68K_TLS_LE8, 39) +ELF_RELOC(R_68K_TLS_DTPMOD32, 40) +ELF_RELOC(R_68K_TLS_DTPREL32, 41) +ELF_RELOC(R_68K_TLS_TPREL32, 42) Index: llvm/include/llvm/IR/CallingConv.h =================================================================== --- llvm/include/llvm/IR/CallingConv.h +++ llvm/include/llvm/IR/CallingConv.h @@ -244,6 +244,9 @@ /// Calling convention used for AMD graphics targets. AMDGPU_Gfx = 100, + /// M68k_INTR - Calling convention used for M68k interrupt routines. + M68k_INTR = 101, + /// The highest possible calling convention ID. Must be some 2^k - 1. MaxID = 1023 }; Index: llvm/include/llvm/Object/ELFObjectFile.h =================================================================== --- llvm/include/llvm/Object/ELFObjectFile.h +++ llvm/include/llvm/Object/ELFObjectFile.h @@ -1120,6 +1120,8 @@ switch (EF.getHeader().e_ident[ELF::EI_CLASS]) { case ELF::ELFCLASS32: switch (EF.getHeader().e_machine) { + case ELF::EM_68K: + return "elf32-m68k"; case ELF::EM_386: return "elf32-i386"; case ELF::EM_IAMCU: @@ -1188,6 +1190,8 @@ template Triple::ArchType ELFObjectFile::getArch() const { bool IsLittleEndian = ELFT::TargetEndianness == support::little; switch (EF.getHeader().e_machine) { + case ELF::EM_68K: + return Triple::m68k; case ELF::EM_386: case ELF::EM_IAMCU: return Triple::x86; Index: llvm/include/llvm/module.modulemap =================================================================== --- llvm/include/llvm/module.modulemap +++ llvm/include/llvm/module.modulemap @@ -69,6 +69,7 @@ textual header "BinaryFormat/ELFRelocs/Hexagon.def" textual header "BinaryFormat/ELFRelocs/i386.def" textual header "BinaryFormat/ELFRelocs/Lanai.def" + textual header "BinaryFormat/ELFRelocs/M68k.def" textual header "BinaryFormat/ELFRelocs/Mips.def" textual header "BinaryFormat/ELFRelocs/MSP430.def" textual header "BinaryFormat/ELFRelocs/PowerPC64.def" Index: llvm/lib/Object/ELF.cpp =================================================================== --- llvm/lib/Object/ELF.cpp +++ llvm/lib/Object/ELF.cpp @@ -22,6 +22,13 @@ StringRef llvm::object::getELFRelocationTypeName(uint32_t Machine, uint32_t Type) { switch (Machine) { + case ELF::EM_68K: + switch (Type) { +#include "llvm/BinaryFormat/ELFRelocs/M68k.def" + default: + break; + } + break; case ELF::EM_X86_64: switch (Type) { #include "llvm/BinaryFormat/ELFRelocs/x86_64.def" Index: llvm/lib/ObjectYAML/ELFYAML.cpp =================================================================== --- llvm/lib/ObjectYAML/ELFYAML.cpp +++ llvm/lib/ObjectYAML/ELFYAML.cpp @@ -689,6 +689,9 @@ case ELF::EM_PPC64: #include "llvm/BinaryFormat/ELFRelocs/PowerPC64.def" break; + case ELF::EM_68K: +#include "llvm/BinaryFormat/ELFRelocs/M68k.def" + break; default: // Nothing to do. break; Index: llvm/lib/Target/M68k/MCTargetDesc/CMakeLists.txt =================================================================== --- llvm/lib/Target/M68k/MCTargetDesc/CMakeLists.txt +++ llvm/lib/Target/M68k/MCTargetDesc/CMakeLists.txt @@ -1,4 +1,9 @@ add_llvm_component_library(LLVMM68kDesc + M68kAsmBackend.cpp + M68kELFObjectWriter.cpp + M68kInstPrinter.cpp + M68kMCAsmInfo.cpp + M68kMCCodeEmitter.cpp M68kMCTargetDesc.cpp LINK_COMPONENTS Index: llvm/lib/Target/M68k/MCTargetDesc/M68kAsmBackend.cpp =================================================================== --- /dev/null +++ llvm/lib/Target/M68k/MCTargetDesc/M68kAsmBackend.cpp @@ -0,0 +1,243 @@ +//===-- M68kAsmBackend.cpp - M68k Assembler Backend ---------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file contains definitions for M68k assembler backend. +/// +//===----------------------------------------------------------------------===// + +#include "MCTargetDesc/M68kBaseInfo.h" +#include "MCTargetDesc/M68kFixupKinds.h" + +#include "llvm/ADT/StringSwitch.h" +#include "llvm/BinaryFormat/ELF.h" +#include "llvm/BinaryFormat/MachO.h" +#include "llvm/MC/MCAsmBackend.h" +#include "llvm/MC/MCELFObjectWriter.h" +#include "llvm/MC/MCExpr.h" +#include "llvm/MC/MCFixupKindInfo.h" +#include "llvm/MC/MCInst.h" +#include "llvm/MC/MCMachObjectWriter.h" +#include "llvm/MC/MCObjectWriter.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/MC/MCSectionCOFF.h" +#include "llvm/MC/MCSectionELF.h" +#include "llvm/MC/MCSectionMachO.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/MathExtras.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; + +namespace { + +class M68kAsmBackend : public MCAsmBackend { + +public: + M68kAsmBackend(const Target &T) : MCAsmBackend(support::big) {} + + unsigned getNumFixupKinds() const override { return 0; } + + void applyFixup(const MCAssembler &Asm, const MCFixup &Fixup, + const MCValue &Target, MutableArrayRef Data, + uint64_t Value, bool IsResolved, + const MCSubtargetInfo *STI) const override { + unsigned Size = 1 << getFixupKindLog2Size(Fixup.getKind()); + + assert(Fixup.getOffset() + Size <= Data.size() && "Invalid fixup offset!"); + + // Check that uppper bits are either all zeros or all ones. + // Specifically ignore overflow/underflow as long as the leakage is + // limited to the lower bits. This is to remain compatible with + // other assemblers. + assert(isIntN(Size * 8 + 1, Value) && + "Value does not fit in the Fixup field"); + + // Write in Big Endian + for (unsigned i = 0; i != Size; ++i) + Data[Fixup.getOffset() + i] = uint8_t(Value >> ((Size - i - 1) * 8)); + } + + bool mayNeedRelaxation(const MCInst &Inst, + const MCSubtargetInfo &STI) const override; + + bool fixupNeedsRelaxation(const MCFixup &Fixup, uint64_t Value, + const MCRelaxableFragment *DF, + const MCAsmLayout &Layout) const override; + + void relaxInstruction(MCInst &Inst, + const MCSubtargetInfo &STI) const override; + + /// Returns the minimum size of a nop in bytes on this target. The assembler + /// will use this to emit excess padding in situations where the padding + /// required for simple alignment would be less than the minimum nop size. + unsigned getMinimumNopSize() const override { return 2; } + + /// Write a sequence of optimal nops to the output, covering \p Count bytes. + /// \return - true on success, false on failure + bool writeNopData(raw_ostream &OS, uint64_t Count) const override; +}; +} // end anonymous namespace + +/// cc—Carry clear GE—Greater than or equal +/// LS—Lower or same PL—Plus +/// CS—Carry set GT—Greater than +/// LT—Less than +/// EQ—Equal HI—Higher +/// MI—Minus VC—Overflow clear +/// LE—Less than or equal +/// NE—Not equal VS—Overflow set +static unsigned getRelaxedOpcodeBranch(const MCInst &Inst) { + unsigned Op = Inst.getOpcode(); + switch (Op) { + default: + return Op; + case M68k::BRA8: + return M68k::BRA16; + case M68k::Bcc8: + return M68k::Bcc16; + case M68k::Bls8: + return M68k::Bls16; + case M68k::Blt8: + return M68k::Blt16; + case M68k::Beq8: + return M68k::Beq16; + case M68k::Bmi8: + return M68k::Bmi16; + case M68k::Bne8: + return M68k::Bne16; + case M68k::Bge8: + return M68k::Bge16; + case M68k::Bcs8: + return M68k::Bcs16; + case M68k::Bpl8: + return M68k::Bpl16; + case M68k::Bgt8: + return M68k::Bgt16; + case M68k::Bhi8: + return M68k::Bhi16; + case M68k::Bvc8: + return M68k::Bvc16; + case M68k::Ble8: + return M68k::Ble16; + case M68k::Bvs8: + return M68k::Bvs16; + } +} + +static unsigned getRelaxedOpcodeArith(const MCInst &Inst) { + unsigned Op = Inst.getOpcode(); + switch (Op) { + default: + return Op; + // NOTE there will be some relaxations for PCD and ARD mem for x20 + } +} + +static unsigned getRelaxedOpcode(const MCInst &Inst) { + unsigned R = getRelaxedOpcodeArith(Inst); + if (R != Inst.getOpcode()) + return R; + return getRelaxedOpcodeBranch(Inst); +} + +bool M68kAsmBackend::mayNeedRelaxation(const MCInst &Inst, + const MCSubtargetInfo &STI) const { + // Branches can always be relaxed in either mode. + if (getRelaxedOpcodeBranch(Inst) != Inst.getOpcode()) + return true; + + // Check if this instruction is ever relaxable. + if (getRelaxedOpcodeArith(Inst) == Inst.getOpcode()) + return false; + + // Check if the relaxable operand has an expression. For the current set of + // relaxable instructions, the relaxable operand is always the last operand. + // NOTE will change for x20 mem + unsigned RelaxableOp = Inst.getNumOperands() - 1; + if (Inst.getOperand(RelaxableOp).isExpr()) + return true; + + return false; +} + +bool M68kAsmBackend::fixupNeedsRelaxation(const MCFixup &Fixup, uint64_t Value, + const MCRelaxableFragment *DF, + const MCAsmLayout &Layout) const { + // TODO Newer CPU can use 32 bit offsets, so check for this when ready + if (!isInt<16>(Value)) { + llvm_unreachable("Cannot relax the instruction, value does not fit"); + } + // Relax if the value is too big for a (signed) i8. This means that byte-wide + // instructions have to matched by default + // + // NOTE + // A branch to the immediately following instruction automatically + // uses the 16-bit displacement format because the 8-bit + // displacement field contains $00 (zero offset). + return Value == 0 || !isInt<8>(Value); +} + +// NOTE Can tblgen help at all here to verify there aren't other instructions +// we can relax? +void M68kAsmBackend::relaxInstruction(MCInst &Inst, + const MCSubtargetInfo &STI) const { + // The only relaxations M68k does is from a 1byte pcrel to a 2byte PCRel. + unsigned RelaxedOp = getRelaxedOpcode(Inst); + + if (RelaxedOp == Inst.getOpcode()) { + SmallString<256> Tmp; + raw_svector_ostream OS(Tmp); + Inst.dump_pretty(OS); + OS << "\n"; + report_fatal_error("unexpected instruction to relax: " + OS.str()); + } + + Inst.setOpcode(RelaxedOp); +} + +bool M68kAsmBackend::writeNopData(raw_ostream &OS, uint64_t Count) const { + // Cannot emit NOP with size being not multiple of 16 bits. + if (Count % 2 != 0) + return false; + + uint64_t NumNops = Count / 2; + for (uint64_t i = 0; i != NumNops; ++i) { + OS << "\x4E\x71"; + } + + return true; +} + +namespace { + +class M68kELFAsmBackend : public M68kAsmBackend { +public: + uint8_t OSABI; + M68kELFAsmBackend(const Target &T, uint8_t OSABI) + : M68kAsmBackend(T), OSABI(OSABI) {} + + std::unique_ptr + createObjectTargetWriter() const override { + return createM68kELFObjectWriter(OSABI); + } +}; + +} // end anonymous namespace + +MCAsmBackend *llvm::createM68kAsmBackend(const Target &T, + const MCSubtargetInfo &STI, + const MCRegisterInfo &MRI, + const MCTargetOptions &Options) { + // assert (TheTriple.getEnvironment() == Triple::GNU); + const Triple &TheTriple = STI.getTargetTriple(); + uint8_t OSABI = MCELFObjectTargetWriter::getOSABI(TheTriple.getOS()); + return new M68kELFAsmBackend(T, OSABI); +} Index: llvm/lib/Target/M68k/MCTargetDesc/M68kBaseInfo.h =================================================================== --- /dev/null +++ llvm/lib/Target/M68k/MCTargetDesc/M68kBaseInfo.h @@ -0,0 +1,261 @@ +//===-- M68kBaseInfo.h - Top level definitions for M68k MC --*- C++ -*-----===// +// +// 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 +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file contains small standalone helper functions and enum definitions +/// for the M68k target useful for the compiler back-end and the MC +/// libraries. As such, it deliberately does not include references to LLVM +/// core code gen types, passes, etc.. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_M68K_MCTARGETDESC_M68KBASEINFO_H +#define LLVM_LIB_TARGET_M68K_MCTARGETDESC_M68KBASEINFO_H + +#include "M68kMCTargetDesc.h" + +#include "llvm/MC/MCExpr.h" +#include "llvm/Support/DataTypes.h" +#include "llvm/Support/ErrorHandling.h" + +#define GET_INSTRINFO_MI_OPS_INFO +#define GET_INSTRINFO_OPERAND_TYPES_ENUM +#define GET_INSTRINFO_LOGICAL_OPERAND_SIZE_MAP +#include "M68kGenInstrInfo.inc" + +namespace llvm { + +namespace M68k { + +/// Enums for memory operand decoding. Supports these forms: +/// (d,An) +/// (d,An,Xn) +/// ([bd,An],Xn,od) +/// ([bd,An,Xn],od) +/// TODO Implement scaling other than 1 +enum { MemDisp = 0, MemBase = 1, MemIndex = 2, MemOuter = 3 }; + +/// Enums for pc-relative memory operand decoding. Supports these forms: +/// (d,PC) +/// (d,PC,Xn) +/// ([bd,PC],Xn,od) +/// ([bd,PC,Xn],od) +enum { PCRelDisp = 0, PCRelIndex = 1, PCRelOuter = 2 }; +} // namespace M68k + +namespace M68kBeads { +enum { + Ctrl = 0x0, + Bits1 = 0x1, + Bits2 = 0x2, + Bits3 = 0x3, + Bits4 = 0x4, + DAReg = 0x5, + DA = 0x6, + Reg = 0x7, + Disp8 = 0x8, + Imm8 = 0x9, + Imm16 = 0xA, + Imm32 = 0xB, + Imm3 = 0xC, +}; + +// Ctrl payload +enum { + Term = 0x0, + Ignore = 0x1, +}; +} // namespace M68kBeads + +/// This namespace holds all of the target specific flags that instruction info +/// tracks. +namespace M68kII { +/// Target Operand Flag enum. +enum TOF { + + MO_NO_FLAG, + + /// On a symbol operand this indicates that the immediate is the absolute + /// address of the symbol. + MO_ABSOLUTE_ADDRESS, + + /// On a symbol operand this indicates that the immediate is the pc-relative + /// address of the symbol. + MO_PC_RELATIVE_ADDRESS, + + /// On a symbol operand this indicates that the immediate is the offset to + /// the GOT entry for the symbol name from the base of the GOT. + /// + /// name@GOT + MO_GOT, + + /// On a symbol operand this indicates that the immediate is the offset to + /// the location of the symbol name from the base of the GOT. + /// + /// name@GOTOFF + MO_GOTOFF, + + /// On a symbol operand this indicates that the immediate is offset to the + /// GOT entry for the symbol name from the current code location. + /// + /// name@GOTPCREL + MO_GOTPCREL, + + /// On a symbol operand this indicates that the immediate is offset to the + /// PLT entry of symbol name from the current code location. + /// + /// name@PLT + MO_PLT, +}; // enum TOF + +// enum { +// //===------------------------------------------------------------------===// +// // Instruction encodings. These are the standard/most common forms for +// // M68k instructions. +// // +// +// // Pseudo - This represents an instruction that is a pseudo instruction +// // or one that has not been implemented yet. It is illegal to code +// generate +// // it, but tolerated for intermediate implementation stages. +// Pseudo = 0, +// FormMask = 15 +// }; + +/// Return true if the specified TargetFlag operand is a reference to a stub +/// for a global, not the global itself. +inline static bool isGlobalStubReference(unsigned char TargetFlag) { + switch (TargetFlag) { + default: + return false; + case M68kII::MO_GOTPCREL: // pc-relative GOT reference. + case M68kII::MO_GOT: // normal GOT reference. + return true; + } +} + +/// Return True if the specified GlobalValue is a direct reference for a +/// symbol. +inline static bool isDirectGlobalReference(unsigned char Flag) { + switch (Flag) { + default: + return false; + case M68kII::MO_NO_FLAG: + case M68kII::MO_ABSOLUTE_ADDRESS: + case M68kII::MO_PC_RELATIVE_ADDRESS: + return true; + } +} + +/// Return true if the specified global value reference is relative to a 32-bit +/// PIC base (M68kISD::GLOBAL_BASE_REG). If this is true, the addressing mode +/// has the PIC base register added in. +inline static bool isGlobalRelativeToPICBase(unsigned char TargetFlag) { + switch (TargetFlag) { + default: + return false; + case M68kII::MO_GOTOFF: // isPICStyleGOT: local global. + case M68kII::MO_GOT: // isPICStyleGOT: other global. + return true; + } +} + +/// Return True if the specified GlobalValue requires PC addressing mode. +inline static bool isPCRelGlobalReference(unsigned char Flag) { + switch (Flag) { + default: + return false; + case M68kII::MO_GOTPCREL: + case M68kII::MO_PC_RELATIVE_ADDRESS: + return true; + } +} + +/// Return True if the Block is referenced using PC +inline static bool isPCRelBlockReference(unsigned char Flag) { + switch (Flag) { + default: + return false; + case M68kII::MO_PC_RELATIVE_ADDRESS: + return true; + } +} + +static inline bool isAddressRegister(unsigned RegNo) { + switch (RegNo) { + case M68k::WA0: + case M68k::WA1: + case M68k::WA2: + case M68k::WA3: + case M68k::WA4: + case M68k::WA5: + case M68k::WA6: + case M68k::WA7: + case M68k::A0: + case M68k::A1: + case M68k::A2: + case M68k::A3: + case M68k::A4: + case M68k::A5: + case M68k::A6: + case M68k::A7: + case M68k::SP: + return true; + default: + return false; + } +} + +static inline bool hasMultiMIOperands(unsigned Op, unsigned LogicalOpIdx) { + return M68k::getLogicalOperandSize(Op, LogicalOpIdx) > 1; +} + +static inline unsigned getMaskedSpillRegister(unsigned order) { + switch (order) { + default: + return 0; + case 0: + return M68k::D0; + case 1: + return M68k::D1; + case 2: + return M68k::D2; + case 3: + return M68k::D3; + case 4: + return M68k::D4; + case 5: + return M68k::D5; + case 6: + return M68k::D6; + case 7: + return M68k::D7; + case 8: + return M68k::A0; + case 9: + return M68k::A1; + case 10: + return M68k::A2; + case 11: + return M68k::A3; + case 12: + return M68k::A4; + case 13: + return M68k::A5; + case 14: + return M68k::A6; + case 15: + return M68k::A7; + } +} + +} // namespace M68kII + +} // namespace llvm + +#endif Index: llvm/lib/Target/M68k/MCTargetDesc/M68kELFObjectWriter.cpp =================================================================== --- /dev/null +++ llvm/lib/Target/M68k/MCTargetDesc/M68kELFObjectWriter.cpp @@ -0,0 +1,126 @@ +//===---------- M68kELFObjectWriter.cpp - M68k ELF Writer ---*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file contains definitions for M68k ELF Writers +/// +//===----------------------------------------------------------------------===// + +#include "MCTargetDesc/M68kFixupKinds.h" +#include "MCTargetDesc/M68kMCTargetDesc.h" + +#include "llvm/BinaryFormat/ELF.h" +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCELFObjectWriter.h" +#include "llvm/MC/MCExpr.h" +#include "llvm/MC/MCValue.h" +#include "llvm/Support/ErrorHandling.h" + +using namespace llvm; + +namespace { +class M68kELFObjectWriter : public MCELFObjectTargetWriter { +public: + M68kELFObjectWriter(uint8_t OSABI); + + ~M68kELFObjectWriter() override; + +protected: + unsigned getRelocType(MCContext &Ctx, const MCValue &Target, + const MCFixup &Fixup, bool IsPCRel) const override; +}; +} // namespace + +M68kELFObjectWriter::M68kELFObjectWriter(uint8_t OSABI) + : MCELFObjectTargetWriter(false, OSABI, ELF::EM_68K, /* RELA */ true) {} + +M68kELFObjectWriter::~M68kELFObjectWriter() {} + +enum M68kRelType { RT_32, RT_16, RT_8 }; + +static M68kRelType +getType(unsigned Kind, MCSymbolRefExpr::VariantKind &Modifier, bool &IsPCRel) { + switch (Kind) { + default: + llvm_unreachable("Unimplemented"); + case FK_Data_4: + case FK_PCRel_4: + return RT_32; + case FK_PCRel_2: + case FK_Data_2: + return RT_16; + case FK_PCRel_1: + case FK_Data_1: + return RT_8; + } +} + +// FIXME Handle reloc differences between pre-M68020 models and successors +unsigned M68kELFObjectWriter::getRelocType(MCContext &Ctx, + const MCValue &Target, + const MCFixup &Fixup, + bool IsPCRel) const { + MCSymbolRefExpr::VariantKind Modifier = Target.getAccessVariant(); + unsigned Kind = Fixup.getKind(); + M68kRelType Type = getType(Kind, Modifier, IsPCRel); + switch (Modifier) { + default: + llvm_unreachable("Unimplemented"); + case MCSymbolRefExpr::VK_None: + switch (Type) { + default: + llvm_unreachable("Unrecognized size"); + case RT_32: + return IsPCRel ? ELF::R_68K_PC32 : ELF::R_68K_32; + case RT_16: + return IsPCRel ? ELF::R_68K_PC16 : ELF::R_68K_16; + case RT_8: + return IsPCRel ? ELF::R_68K_PC8 : ELF::R_68K_8; + } + case MCSymbolRefExpr::VK_GOTPCREL: + switch (Type) { + default: + llvm_unreachable("Unrecognized size"); + case RT_32: + return ELF::R_68K_GOTPCREL32; + case RT_16: + return ELF::R_68K_GOTPCREL16; + case RT_8: + return ELF::R_68K_GOTPCREL8; + } + case MCSymbolRefExpr::VK_GOTOFF: + assert(!IsPCRel); + switch (Type) { + default: + llvm_unreachable("Unrecognized size"); + case RT_32: + return ELF::R_68K_GOTOFF32; + case RT_16: + return ELF::R_68K_GOTOFF16; + case RT_8: + return ELF::R_68K_GOTOFF8; + } + case MCSymbolRefExpr::VK_PLT: + switch (Type) { + default: + llvm_unreachable("Unrecognized size"); + case RT_32: + return ELF::R_68K_PLT32; + case RT_16: + return ELF::R_68K_PLT16; + case RT_8: + return ELF::R_68K_PLT8; + } + } +} + +std::unique_ptr +llvm::createM68kELFObjectWriter(uint8_t OSABI) { + return std::make_unique(OSABI); +} Index: llvm/lib/Target/M68k/MCTargetDesc/M68kFixupKinds.h =================================================================== --- /dev/null +++ llvm/lib/Target/M68k/MCTargetDesc/M68kFixupKinds.h @@ -0,0 +1,56 @@ +//===-- M68kFixupKinds.h - M68k Specific Fixup Entries ------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file contains M68k specific fixup entries. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_M68k_MCTARGETDESC_M68kFIXUPKINDS_H +#define LLVM_LIB_TARGET_M68k_MCTARGETDESC_M68kFIXUPKINDS_H + +#include "llvm/MC/MCFixup.h" + +namespace llvm { +static inline unsigned getFixupKindLog2Size(unsigned Kind) { + switch (Kind) { + default: + llvm_unreachable("invalid fixup kind!"); + case FK_PCRel_1: + case FK_SecRel_1: + case FK_Data_1: + return 0; + case FK_PCRel_2: + case FK_SecRel_2: + case FK_Data_2: + return 1; + case FK_PCRel_4: + case FK_SecRel_4: + case FK_Data_4: + return 2; + } +} + +static inline MCFixupKind getFixupForSize(unsigned Size, bool isPCRel) { + switch (Size) { + default: + llvm_unreachable("Invalid generic fixup size!"); + case 8: + return isPCRel ? FK_PCRel_1 : FK_Data_1; + case 16: + return isPCRel ? FK_PCRel_2 : FK_Data_2; + case 32: + return isPCRel ? FK_PCRel_4 : FK_Data_4; + case 64: + return isPCRel ? FK_PCRel_8 : FK_Data_8; + } +} + +} // namespace llvm + +#endif Index: llvm/lib/Target/M68k/MCTargetDesc/M68kInstPrinter.h =================================================================== --- /dev/null +++ llvm/lib/Target/M68k/MCTargetDesc/M68kInstPrinter.h @@ -0,0 +1,169 @@ +//===-- M68kInstPrinter.h - Convert M68k MCInst to asm ------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file contains declarations for an M68k MCInst printer. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_M68K_INSTPRINTER_M68KINSTPRINTER_H +#define LLVM_LIB_TARGET_M68K_INSTPRINTER_M68KINSTPRINTER_H + +#include "llvm/MC/MCInstPrinter.h" + +namespace llvm { + +class TargetMachine; + +class M68kInstPrinter : public MCInstPrinter { +public: + M68kInstPrinter(const MCAsmInfo &MAI, const MCInstrInfo &MII, + const MCRegisterInfo &MRI) + : MCInstPrinter(MAI, MII, MRI) {} + + // Autogenerated by tblgen. + void printInstruction(const MCInst *MI, uint64_t Address, raw_ostream &O); + static const char *getRegisterName(unsigned RegNo); + + void printRegName(raw_ostream &OS, unsigned RegNo) const override; + void printInst(const MCInst *MI, uint64_t Address, StringRef Annot, + const MCSubtargetInfo &STI, raw_ostream &O) override; + + bool printAliasInstr(const MCInst *MI, uint64_t Address, raw_ostream &OS); + void printCustomAliasOperand(const MCInst *MI, unsigned OpIdx, + unsigned PrintMethodIdx, raw_ostream &O); + + std::pair getMnemonic(const MCInst *MI) override; + +private: + void printOperand(const MCInst *MI, unsigned opNum, raw_ostream &O); + void printImmediate(const MCInst *MI, unsigned opNum, raw_ostream &O); + /// Print register mask for MOVEM instruction in order D0-D7,A0-A7 + void printMoveMask(const MCInst *MI, unsigned opNum, raw_ostream &O); + /// Print register mask for MOVEM instruction in order A7-A0,D7-D0 + void printMoveMaskR(const MCInst *MI, unsigned opNum, raw_ostream &O); + void printDisp(const MCInst *MI, unsigned opNum, raw_ostream &O); + void printARIMem(const MCInst *MI, unsigned opNum, raw_ostream &O); + void printARIPIMem(const MCInst *MI, unsigned opNum, raw_ostream &O); + void printARIPDMem(const MCInst *MI, unsigned opNum, raw_ostream &O); + void printARIDMem(const MCInst *MI, unsigned opNum, raw_ostream &O); + void printARIIMem(const MCInst *MI, unsigned opNum, raw_ostream &O); + void printAbsMem(const MCInst *MI, unsigned opNum, raw_ostream &O); + void printPCDMem(const MCInst *MI, uint64_t Address, unsigned opNum, + raw_ostream &O); + void printPCIMem(const MCInst *MI, uint64_t Address, unsigned opNum, + raw_ostream &O); + + //===----------------------------------------------------------------------===// + // Specializations + //===----------------------------------------------------------------------===// + // + void printPCRelImm(const MCInst *MI, uint64_t Address, unsigned opNum, + raw_ostream &O) { + printOperand(MI, opNum, O); + } + + void printARI8Mem(const MCInst *MI, unsigned opNum, raw_ostream &O) { + printARIMem(MI, opNum, O); + } + void printARI16Mem(const MCInst *MI, unsigned opNum, raw_ostream &O) { + printARIMem(MI, opNum, O); + } + void printARI32Mem(const MCInst *MI, unsigned opNum, raw_ostream &O) { + printARIMem(MI, opNum, O); + } + + void printARIPI8Mem(const MCInst *MI, unsigned opNum, raw_ostream &O) { + printARIPIMem(MI, opNum, O); + } + void printARIPI16Mem(const MCInst *MI, unsigned opNum, raw_ostream &O) { + printARIPIMem(MI, opNum, O); + } + void printARIPI32Mem(const MCInst *MI, unsigned opNum, raw_ostream &O) { + printARIPIMem(MI, opNum, O); + } + + void printARIPD8Mem(const MCInst *MI, unsigned opNum, raw_ostream &O) { + printARIPDMem(MI, opNum, O); + } + void printARIPD16Mem(const MCInst *MI, unsigned opNum, raw_ostream &O) { + printARIPDMem(MI, opNum, O); + } + void printARIPD32Mem(const MCInst *MI, unsigned opNum, raw_ostream &O) { + printARIPDMem(MI, opNum, O); + } + + void printARID8Mem(const MCInst *MI, unsigned opNum, raw_ostream &O) { + printARIDMem(MI, opNum, O); + } + void printARID16Mem(const MCInst *MI, unsigned opNum, raw_ostream &O) { + printARIDMem(MI, opNum, O); + } + void printARID32Mem(const MCInst *MI, unsigned opNum, raw_ostream &O) { + printARIDMem(MI, opNum, O); + } + + void printARII8Mem(const MCInst *MI, unsigned opNum, raw_ostream &O) { + printARIIMem(MI, opNum, O); + } + void printARII16Mem(const MCInst *MI, unsigned opNum, raw_ostream &O) { + printARIIMem(MI, opNum, O); + } + void printARII32Mem(const MCInst *MI, unsigned opNum, raw_ostream &O) { + printARIIMem(MI, opNum, O); + } + + void printAS8Mem(const MCInst *MI, unsigned opNum, raw_ostream &O) { + printAbsMem(MI, opNum, O); + } + void printAS16Mem(const MCInst *MI, unsigned opNum, raw_ostream &O) { + printAbsMem(MI, opNum, O); + } + void printAS32Mem(const MCInst *MI, unsigned opNum, raw_ostream &O) { + printAbsMem(MI, opNum, O); + } + + void printAL8Mem(const MCInst *MI, unsigned opNum, raw_ostream &O) { + printAbsMem(MI, opNum, O); + } + void printAL16Mem(const MCInst *MI, unsigned opNum, raw_ostream &O) { + printAbsMem(MI, opNum, O); + } + void printAL32Mem(const MCInst *MI, unsigned opNum, raw_ostream &O) { + printAbsMem(MI, opNum, O); + } + + void printPCD8Mem(const MCInst *MI, uint64_t Address, unsigned opNum, + raw_ostream &O) { + printPCDMem(MI, Address, opNum, O); + } + void printPCD16Mem(const MCInst *MI, uint64_t Address, unsigned opNum, + raw_ostream &O) { + printPCDMem(MI, Address, opNum, O); + } + void printPCD32Mem(const MCInst *MI, uint64_t Address, unsigned opNum, + raw_ostream &O) { + printPCDMem(MI, Address, opNum, O); + } + + void printPCI8Mem(const MCInst *MI, uint64_t Address, unsigned opNum, + raw_ostream &O) { + printPCIMem(MI, Address, opNum, O); + } + void printPCI16Mem(const MCInst *MI, uint64_t Address, unsigned opNum, + raw_ostream &O) { + printPCIMem(MI, Address, opNum, O); + } + void printPCI32Mem(const MCInst *MI, uint64_t Address, unsigned opNum, + raw_ostream &O) { + printPCIMem(MI, Address, opNum, O); + } +}; +} // end namespace llvm + +#endif Index: llvm/lib/Target/M68k/MCTargetDesc/M68kInstPrinter.cpp =================================================================== --- /dev/null +++ llvm/lib/Target/M68k/MCTargetDesc/M68kInstPrinter.cpp @@ -0,0 +1,220 @@ +//===-- M68kInstPrinter.cpp - Convert M68k MCInst to asm ----*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file contains definitions for an M68k MCInst printer. +/// +//===----------------------------------------------------------------------===// + +// TODO Conform with all supported Motorola ASM syntax +// Motorola's assembly has several syntax variants, especially on +// addressing modes. +// For example, you can write pc indirect w/ displacement as +// `x(%pc)`, where `x` is the displacement imm, or `(x,%pc)`. +// Currently we're picking the variant that is different from +// GCC, albeit being recognizable by GNU AS. +// Not sure what is the impact now (e.g. some syntax might +// not be recognized by some old consoles' toolchains, in which +// case we can not use our integrated assembler), but either way, +// it will be great to support all of the variants in the future. + +#include "M68kInstPrinter.h" +#include "M68kBaseInfo.h" + +#include "llvm/ADT/StringExtras.h" +#include "llvm/MC/MCExpr.h" +#include "llvm/MC/MCInst.h" +#include "llvm/MC/MCInstrInfo.h" +#include "llvm/MC/MCSymbol.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; + +#define DEBUG_TYPE "asm-printer" + +#define PRINT_ALIAS_INSTR +#include "M68kGenAsmWriter.inc" + +void M68kInstPrinter::printRegName(raw_ostream &OS, unsigned RegNo) const { + OS << "%" << getRegisterName(RegNo); +} + +void M68kInstPrinter::printInst(const MCInst *MI, uint64_t Address, + StringRef Annot, const MCSubtargetInfo &STI, + raw_ostream &O) { + if (!printAliasInstr(MI, Address, O)) { + printInstruction(MI, Address, O); + } + printAnnotation(O, Annot); +} + +void M68kInstPrinter::printOperand(const MCInst *MI, unsigned OpNo, + raw_ostream &O) { + const MCOperand &MO = MI->getOperand(OpNo); + if (MO.isReg()) { + printRegName(O, MO.getReg()); + return; + } + + if (MO.isImm()) { + printImmediate(MI, OpNo, O); + return; + } + + assert(MO.isExpr() && "Unknown operand kind in printOperand"); + MO.getExpr()->print(O, &MAI); +} + +void M68kInstPrinter::printImmediate(const MCInst *MI, unsigned opNum, + raw_ostream &O) { + const MCOperand &MO = MI->getOperand(opNum); + if (MO.isImm()) { + O << '#' << MO.getImm(); + } else if (MO.isExpr()) { + O << '#'; + MO.getExpr()->print(O, &MAI); + } else { + llvm_unreachable("Unknown immediate kind"); + } +} + +void M68kInstPrinter::printMoveMask(const MCInst *MI, unsigned opNum, + raw_ostream &O) { + unsigned Mask = MI->getOperand(opNum).getImm(); + assert((Mask & 0xFFFF) == Mask && "Mask is always 16 bits"); + + // A move mask is splitted into two parts: + // bits 0 ~ 7 correspond to D0 ~ D7 regs + // bits 8 ~ 15 correspond to A0 ~ A7 regs + // + // In the assembly syntax, we want to use a dash to replace + // a continuous range of registers. For example, if the bit + // mask is 0b101110, we want to print "D1-D3,D5" instead of + // "D1,D2,D3,D4,D5". + // + // However, we don't want a dash to cross between data registers + // and address registers (i.e. there shouldn't be a dash crossing + // bit 7 and 8) since that is not really intuitive. So we simply + // print the data register part (bit 0~7) and address register part + // separately. + uint8_t HalfMask; + unsigned Reg; + for (int s = 0; s < 16; s += 8) { + HalfMask = (Mask >> s) & 0xFF; + // Print separation comma only if + // both data & register parts have bit(s) set + if (s != 0 && (Mask & 0xFF) && HalfMask) + O << ','; + + for (int i = 0; HalfMask; ++i) { + if ((HalfMask >> i) & 0b1) { + HalfMask ^= 0b1 << i; + Reg = M68kII::getMaskedSpillRegister(i + s); + printRegName(O, Reg); + + int j = i; + while ((HalfMask >> (j + 1)) & 0b1) { + HalfMask ^= 0b1 << ++j; + } + + if (j != i) { + O << '-'; + Reg = M68kII::getMaskedSpillRegister(j + s); + printRegName(O, Reg); + } + + i = j; + + if (HalfMask) { + O << ','; + } + } + } + } +} + +void M68kInstPrinter::printDisp(const MCInst *MI, unsigned opNum, + raw_ostream &O) { + const MCOperand &Op = MI->getOperand(opNum); + if (Op.isImm()) { + O << Op.getImm(); + return; + } + assert(Op.isExpr() && "Unknown operand kind in printOperand"); + Op.getExpr()->print(O, &MAI); +} + +void M68kInstPrinter::printARIMem(const MCInst *MI, unsigned opNum, + raw_ostream &O) { + O << '('; + printOperand(MI, opNum, O); + O << ')'; +} + +void M68kInstPrinter::printARIPIMem(const MCInst *MI, unsigned opNum, + raw_ostream &O) { + O << "("; + printOperand(MI, opNum, O); + O << ")+"; +} + +void M68kInstPrinter::printARIPDMem(const MCInst *MI, unsigned opNum, + raw_ostream &O) { + O << "-("; + printOperand(MI, opNum, O); + O << ")"; +} + +void M68kInstPrinter::printARIDMem(const MCInst *MI, unsigned opNum, + raw_ostream &O) { + O << '('; + printDisp(MI, opNum + M68k::MemDisp, O); + O << ','; + printOperand(MI, opNum + M68k::MemBase, O); + O << ')'; +} + +void M68kInstPrinter::printARIIMem(const MCInst *MI, unsigned opNum, + raw_ostream &O) { + O << '('; + printDisp(MI, opNum + M68k::MemDisp, O); + O << ','; + printOperand(MI, opNum + M68k::MemBase, O); + O << ','; + printOperand(MI, opNum + M68k::MemIndex, O); + O << ')'; +} + +// NOTE forcing (W,L) size available since M68020 only +void M68kInstPrinter::printAbsMem(const MCInst *MI, unsigned opNum, + raw_ostream &O) { + const MCOperand &MO = MI->getOperand(opNum); + if (MO.isImm()) { + // ??? Print it in hex? + O << (unsigned int)MO.getImm(); + } else { + printOperand(MI, opNum, O); + } +} + +void M68kInstPrinter::printPCDMem(const MCInst *MI, uint64_t Address, + unsigned opNum, raw_ostream &O) { + O << '('; + printDisp(MI, opNum + M68k::PCRelDisp, O); + O << ",%pc)"; +} + +void M68kInstPrinter::printPCIMem(const MCInst *MI, uint64_t Address, + unsigned opNum, raw_ostream &O) { + O << '('; + printDisp(MI, opNum + M68k::PCRelDisp, O); + O << ",%pc,"; + printOperand(MI, opNum + M68k::PCRelIndex, O); + O << ')'; +} Index: llvm/lib/Target/M68k/MCTargetDesc/M68kMCAsmInfo.h =================================================================== --- /dev/null +++ llvm/lib/Target/M68k/MCTargetDesc/M68kMCAsmInfo.h @@ -0,0 +1,31 @@ +//===-- M68kMCAsmInfo.h - M68k Asm Info --------------------*- C++ -*--===// +// +// 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 +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file contains the declarations of the M68k MCAsmInfo properties. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_M68K_MCTARGETDESC_M68KMCASMINFO_H +#define LLVM_LIB_TARGET_M68K_MCTARGETDESC_M68KMCASMINFO_H + +#include "llvm/MC/MCAsmInfoELF.h" + +namespace llvm { +class Triple; + +class M68kELFMCAsmInfo : public MCAsmInfoELF { + void anchor() override; + +public: + explicit M68kELFMCAsmInfo(const Triple &Triple); +}; + +} // namespace llvm + +#endif Index: llvm/lib/Target/M68k/MCTargetDesc/M68kMCAsmInfo.cpp =================================================================== --- /dev/null +++ llvm/lib/Target/M68k/MCTargetDesc/M68kMCAsmInfo.cpp @@ -0,0 +1,38 @@ +//===-- M68kMCAsmInfo.cpp - M68k Asm Properties -------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file contains the definitions of the M68k MCAsmInfo properties. +/// +//===----------------------------------------------------------------------===// + +#include "M68kMCAsmInfo.h" + +#include "llvm/ADT/Triple.h" + +using namespace llvm; + +void M68kELFMCAsmInfo::anchor() {} + +M68kELFMCAsmInfo::M68kELFMCAsmInfo(const Triple &T) { + CodePointerSize = 4; + CalleeSaveStackSlotSize = 4; + + IsLittleEndian = false; + + // Filled with NOP (0b0100_1110_0111_0001) + TextAlignFillValue = 0x4e71; + + // Debug Information + SupportsDebugInformation = true; + + // Exceptions handling + ExceptionsType = ExceptionHandling::DwarfCFI; + + CommentString = ";"; +} Index: llvm/lib/Target/M68k/MCTargetDesc/M68kMCCodeEmitter.cpp =================================================================== --- /dev/null +++ llvm/lib/Target/M68k/MCTargetDesc/M68kMCCodeEmitter.cpp @@ -0,0 +1,399 @@ +//===-- M68kMCCodeEmitter.cpp - Convert M68k code emitter ---*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file contains defintions for M68k code emitter. +/// +//===----------------------------------------------------------------------===// + +#include "MCTargetDesc/M68kBaseInfo.h" +#include "MCTargetDesc/M68kFixupKinds.h" +#include "MCTargetDesc/M68kMCTargetDesc.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" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/MC/MCSymbol.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" + +namespace llvm { +namespace M68k { +// Forward declarations +const uint8_t *getMCInstrBeads(unsigned); +} // end namespace M68k +} // end namespace llvm + +using namespace llvm; + +#define DEBUG_TYPE "m68k-mccodeemitter" + +namespace { +class M68kMCCodeEmitter : public MCCodeEmitter { + M68kMCCodeEmitter(const M68kMCCodeEmitter &) = delete; + void operator=(const M68kMCCodeEmitter &) = delete; + const MCInstrInfo &MCII; + MCContext &Ctx; + +public: + M68kMCCodeEmitter(const MCInstrInfo &mcii, MCContext &ctx) + : MCII(mcii), Ctx(ctx) {} + + ~M68kMCCodeEmitter() override {} + + // TableGen'erated function + const uint8_t *getGenInstrBeads(const MCInst &MI) const { + return M68k::getMCInstrBeads(MI.getOpcode()); + } + + unsigned encodeBits(unsigned ThisByte, uint8_t Bead, const MCInst &MI, + const MCInstrDesc &Desc, uint64_t &Buffer, + unsigned Offset, SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + + unsigned encodeReg(unsigned ThisByte, uint8_t Bead, const MCInst &MI, + const MCInstrDesc &Desc, uint64_t &Buffer, unsigned Offset, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + + unsigned encodeImm(unsigned ThisByte, uint8_t Bead, const MCInst &MI, + const MCInstrDesc &Desc, uint64_t &Buffer, unsigned Offset, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + + void encodeInstruction(const MCInst &MI, raw_ostream &OS, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const override; +}; + +} // end anonymous namespace + +unsigned M68kMCCodeEmitter::encodeBits(unsigned ThisByte, uint8_t Bead, + const MCInst &MI, + const MCInstrDesc &Desc, + uint64_t &Buffer, unsigned Offset, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + unsigned Num = 0; + switch (Bead & 0xF) { + case M68kBeads::Bits1: + Num = 1; + break; + case M68kBeads::Bits2: + Num = 2; + break; + case M68kBeads::Bits3: + Num = 3; + break; + case M68kBeads::Bits4: + Num = 4; + break; + } + unsigned char Val = (Bead & 0xF0) >> 4; + + LLVM_DEBUG(dbgs() << "\tEncodeBits" + << " Num: " << Num << " Val: 0x"); + LLVM_DEBUG(dbgs().write_hex(Val) << "\n"); + + Buffer |= (Val << Offset); + + return Num; +} + +unsigned M68kMCCodeEmitter::encodeReg(unsigned ThisByte, uint8_t Bead, + const MCInst &MI, const MCInstrDesc &Desc, + uint64_t &Buffer, unsigned Offset, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + bool DA, Reg; + switch (Bead & 0xF) { + case M68kBeads::DAReg: + Reg = true; + DA = true; + break; + case M68kBeads::DA: + Reg = false; + DA = true; + break; + case M68kBeads::Reg: + Reg = true; + DA = false; + break; + } + + unsigned Op = (Bead & 0x70) >> 4; + bool Alt = (Bead & 0x80); + LLVM_DEBUG(dbgs() << "\tEncodeReg" + << " Op: " << Op << ", DA: " << DA << ", Reg: " << Reg + << ", Alt: " << Alt << "\n"); + + auto MIOpIdx = M68k::getLogicalOperandIdx(MI.getOpcode(), Op); + bool isPCRel = Desc.OpInfo[MIOpIdx].OperandType == MCOI::OPERAND_PCREL; + + MCOperand MCO; + if (M68kII::hasMultiMIOperands(MI.getOpcode(), Op)) { + if (isPCRel) { + assert(Alt && + "PCRel addresses use Alt bead register encoding by default"); + MCO = MI.getOperand(MIOpIdx + M68k::PCRelIndex); + } else { + MCO = MI.getOperand(MIOpIdx + (Alt ? M68k::MemIndex : M68k::MemBase)); + } + } else { + assert(!Alt && "You cannot use Alt register with a simple operand"); + MCO = MI.getOperand(MIOpIdx); + } + + unsigned RegNum = MCO.getReg(); + auto RI = Ctx.getRegisterInfo(); + + unsigned Written = 0; + if (Reg) { + uint32_t Val = RI->getEncodingValue(RegNum); + Buffer |= Val << Offset; + Offset += 3; + Written += 3; + } + + if (DA) { + Buffer |= (char)M68kII::isAddressRegister(RegNum) << Offset; + Written++; + } + + return Written; +} + +static unsigned EmitConstant(uint64_t Val, unsigned Size, unsigned Pad, + uint64_t &Buffer, unsigned Offset) { + assert(Size + Offset <= 64 && isUIntN(Size, Val) && "Value does not fit"); + + // Pad the instruction with zeros if any + // FIXME Emit zeros in the padding, since there might be trash in the buffer. + Size += Pad; + + // Writing Value in host's endianness + Buffer |= Val << Offset; + return Size; +} + +unsigned M68kMCCodeEmitter::encodeImm(unsigned ThisByte, uint8_t Bead, + const MCInst &MI, const MCInstrDesc &Desc, + uint64_t &Buffer, unsigned Offset, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + unsigned ThisWord = ThisByte / 2; + unsigned Size = 0; + unsigned Pad = 0; + unsigned FixOffset = 0; + int64_t Addendum = 0; + bool NoExpr = false; + + unsigned Type = Bead & 0xF; + unsigned Op = (Bead & 0x70) >> 4; + bool Alt = (Bead & 0x80); + + auto MIOpIdx = M68k::getLogicalOperandIdx(MI.getOpcode(), Op); + bool isPCRel = Desc.OpInfo[MIOpIdx].OperandType == MCOI::OPERAND_PCREL; + + // The PC value upon instruction reading of a short jump will point to the + // next instruction, thus we need to compensate 2 bytes, which is the diff + // between the patch point and the PC. + if (isPCRel && ThisWord == 0) { + Addendum -= 2; + } + + switch (Type) { + // ??? what happens if it is not byte aligned + // ??? is it even possible + case M68kBeads::Disp8: + Size = 8; + Pad = 0; + FixOffset = ThisByte + 1; + Addendum += 1; + break; + case M68kBeads::Imm8: + Size = 8; + Pad = 8; + FixOffset = ThisByte; + break; + case M68kBeads::Imm16: + Size = 16; + Pad = 0; + FixOffset = ThisByte; + break; + case M68kBeads::Imm32: + Size = 32; + Pad = 0; + FixOffset = ThisByte; + break; + case M68kBeads::Imm3: + Size = 3; + Pad = 0; + NoExpr = true; + break; + } + + LLVM_DEBUG(dbgs() << "\tEncodeImm" + << " Op: " << Op << ", Size: " << Size << ", Alt: " << Alt + << "\n"); + + MCOperand MCO; + if (M68kII::hasMultiMIOperands(MI.getOpcode(), Op)) { + + if (isPCRel) { + assert(!Alt && "You cannot use ALT operand with PCRel"); + MCO = MI.getOperand(MIOpIdx + M68k::PCRelDisp); + } else { + MCO = MI.getOperand(MIOpIdx + (Alt ? M68k::MemOuter : M68k::MemDisp)); + } + + if (MCO.isExpr()) { + assert(!NoExpr && "Cannot use expression here"); + const MCExpr *Expr = MCO.getExpr(); + + // This only makes sense for PCRel instructions since PC points to the + // extension word and Disp8 for example is right justified and requires + // correction. E.g. R_68K_PC32 is calculated as S + A - P, P for Disp8 + // will be EXTENSION_WORD + 1 thus we need to have A equal to 1 to + // compensate. + // TODO count extension words + if (isPCRel && Addendum != 0) { + Expr = MCBinaryExpr::createAdd( + Expr, MCConstantExpr::create(Addendum, Ctx), Ctx); + } + + Fixups.push_back(MCFixup::create( + FixOffset, Expr, getFixupForSize(Size, isPCRel), MI.getLoc())); + // Write zeros + return EmitConstant(0, Size, Pad, Buffer, Offset); + } + + } else { + // assert (!Alt && "You cannot use Alt immediate with a simple operand"); + MCO = MI.getOperand(MIOpIdx); + if (MCO.isExpr()) { + assert(!NoExpr && "Cannot use expression here"); + const MCExpr *Expr = MCO.getExpr(); + + if (Addendum != 0) { + Expr = MCBinaryExpr::createAdd( + Expr, MCConstantExpr::create(Addendum, Ctx), Ctx); + } + + Fixups.push_back(MCFixup::create( + FixOffset, Expr, getFixupForSize(Size, isPCRel), MI.getLoc())); + // Write zeros + return EmitConstant(0, Size, Pad, Buffer, Offset); + } + } + + int64_t I = MCO.getImm(); + + // Store 8 as 0, thus making range 1-8 + if (Type == M68kBeads::Imm3 && Alt) { + assert(I && "Cannot encode Alt Imm3 zero value"); + I %= 8; + } else { + assert(isIntN(Size, I)); + } + + uint64_t Imm = I; + + // 32 bit Imm requires HI16 first then LO16 + if (Size == 32) { + Offset += EmitConstant((Imm >> 16) & 0xFFFF, 16, Pad, Buffer, Offset); + EmitConstant(Imm & 0xFFFF, 16, Pad, Buffer, Offset); + return Size; + } + + return EmitConstant(Imm & (UINT64_MAX >> (64 - Size)), Size, Pad, Buffer, + Offset); +} + +#include "M68kGenMCCodeBeads.inc" + +void M68kMCCodeEmitter::encodeInstruction(const MCInst &MI, raw_ostream &OS, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + unsigned Opcode = MI.getOpcode(); + const MCInstrDesc &Desc = MCII.get(Opcode); + + LLVM_DEBUG(dbgs() << "EncodeInstruction: " << MCII.getName(Opcode) << "(" + << Opcode << ")\n"); + + const uint8_t *Beads = getGenInstrBeads(MI); + if (!Beads || !*Beads) { + llvm_unreachable("*** Instruction does not have Beads defined"); + } + + uint64_t Buffer = 0; + unsigned Offset = 0; + unsigned ThisByte = 0; + + while (*Beads) { + uint8_t Bead = *Beads; + Beads++; + + // Check for control beads + if (!(Bead & 0xF)) { + switch (Bead >> 4) { + case M68kBeads::Ignore: + continue; + } + } + + switch (Bead & 0xF) { + default: + llvm_unreachable("Unknown Bead code"); + break; + case M68kBeads::Bits1: + case M68kBeads::Bits2: + case M68kBeads::Bits3: + case M68kBeads::Bits4: + Offset += + encodeBits(ThisByte, Bead, MI, Desc, Buffer, Offset, Fixups, STI); + break; + case M68kBeads::DAReg: + case M68kBeads::DA: + case M68kBeads::Reg: + Offset += + encodeReg(ThisByte, Bead, MI, Desc, Buffer, Offset, Fixups, STI); + break; + case M68kBeads::Disp8: + case M68kBeads::Imm8: + case M68kBeads::Imm16: + case M68kBeads::Imm32: + case M68kBeads::Imm3: + Offset += + encodeImm(ThisByte, Bead, MI, Desc, Buffer, Offset, Fixups, STI); + break; + } + + // Since M68k is Big Endian we need to rotate each instruction word + while (Offset / 16) { + OS.write((char)((Buffer >> 8) & 0xFF)); + OS.write((char)((Buffer >> 0) & 0xFF)); + Buffer >>= 16; + Offset -= 16; + ThisByte += 2; + } + } + + assert(Offset == 0 && "M68k Instructions are % 2 bytes"); + assert((ThisByte && !(ThisByte % 2)) && "M68k Instructions are % 2 bytes"); +} + +MCCodeEmitter *llvm::createM68kMCCodeEmitter(const MCInstrInfo &MCII, + const MCRegisterInfo &MRI, + MCContext &Ctx) { + return new M68kMCCodeEmitter(MCII, Ctx); +} Index: llvm/lib/Target/M68k/MCTargetDesc/M68kMCTargetDesc.h =================================================================== --- /dev/null +++ llvm/lib/Target/M68k/MCTargetDesc/M68kMCTargetDesc.h @@ -0,0 +1,63 @@ +//===-- M68kMCTargetDesc.h - M68k Target Descriptions -------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file provides M68k specific target descriptions. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_M68K_MCTARGETDESC_M68KMCTARGETDESC_H +#define LLVM_LIB_TARGET_M68K_MCTARGETDESC_M68KMCTARGETDESC_H + +#include "llvm/MC/MCInstrDesc.h" +#include "llvm/MC/MCObjectWriter.h" +#include "llvm/Support/DataTypes.h" + +namespace llvm { +class MCAsmBackend; +class MCCodeEmitter; +class MCContext; +class MCInstrInfo; +class MCRegisterInfo; +class MCSubtargetInfo; +class MCRelocationInfo; +class MCTargetOptions; +class Target; +class Triple; +class StringRef; +class raw_ostream; +class raw_pwrite_stream; + +extern Target TheM68kTarget; + +MCAsmBackend *createM68kAsmBackend(const Target &T, const MCSubtargetInfo &STI, + const MCRegisterInfo &MRI, + const MCTargetOptions &Options); + +MCCodeEmitter *createM68kMCCodeEmitter(const MCInstrInfo &MCII, + const MCRegisterInfo &MRI, + MCContext &Ctx); + +/// Construct an M68k ELF object writer. +std::unique_ptr createM68kELFObjectWriter(uint8_t OSABI); + +} // namespace llvm + +// Defines symbolic names for M68k registers. This defines a mapping from +// register name to register number. +#define GET_REGINFO_ENUM +#include "M68kGenRegisterInfo.inc" + +// Defines symbolic names for the M68k instructions. +#define GET_INSTRINFO_ENUM +#include "M68kGenInstrInfo.inc" + +#define GET_SUBTARGETINFO_ENUM +#include "M68kGenSubtargetInfo.inc" + +#endif Index: llvm/lib/Target/M68k/MCTargetDesc/M68kMCTargetDesc.cpp =================================================================== --- llvm/lib/Target/M68k/MCTargetDesc/M68kMCTargetDesc.cpp +++ llvm/lib/Target/M68k/MCTargetDesc/M68kMCTargetDesc.cpp @@ -11,7 +11,126 @@ /// //===----------------------------------------------------------------------===// -/// This is just a placeholder to make current -/// commit buildable. Body of this function will -/// be filled in later commits -extern "C" void LLVMInitializeM68kTargetMC() {} +#include "M68kMCTargetDesc.h" + +#include "M68kMCAsmInfo.h" + +#include "M68kInstPrinter.h" + +#include "llvm/MC/MCELFStreamer.h" +#include "llvm/MC/MCInstPrinter.h" +#include "llvm/MC/MCInstrInfo.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/MC/MCSymbol.h" +#include "llvm/MC/MachineLocation.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/FormattedStream.h" +#include "llvm/Support/TargetRegistry.h" + +using namespace llvm; + +#define GET_INSTRINFO_MC_DESC +#include "M68kGenInstrInfo.inc" + +#define GET_SUBTARGETINFO_MC_DESC +#include "M68kGenSubtargetInfo.inc" + +#define GET_REGINFO_MC_DESC +#include "M68kGenRegisterInfo.inc" + +// TODO Implement feature set parsing logics +static std::string ParseM68kTriple(const Triple &TT, StringRef CPU) { + return ""; +} + +static MCInstrInfo *createM68kMCInstrInfo() { + MCInstrInfo *X = new MCInstrInfo(); + InitM68kMCInstrInfo(X); // defined in M68kGenInstrInfo.inc + return X; +} + +static MCRegisterInfo *createM68kMCRegisterInfo(const Triple &TT) { + MCRegisterInfo *X = new MCRegisterInfo(); + InitM68kMCRegisterInfo(X, llvm::M68k::A0, 0, 0, llvm::M68k::PC); + return X; +} + +static MCSubtargetInfo *createM68kMCSubtargetInfo(const Triple &TT, + StringRef CPU, StringRef FS) { + std::string ArchFS = ParseM68kTriple(TT, CPU); + if (!FS.empty()) { + if (!ArchFS.empty()) { + ArchFS = (ArchFS + "," + FS).str(); + } else { + ArchFS = FS.str(); + } + } + return createM68kMCSubtargetInfoImpl(TT, CPU, /*TuneCPU=*/CPU, ArchFS); +} + +static MCAsmInfo *createM68kMCAsmInfo(const MCRegisterInfo &MRI, + const Triple &TT, + const MCTargetOptions &TO) { + MCAsmInfo *MAI = new M68kELFMCAsmInfo(TT); + + // Initialize initial frame state. + // Calculate amount of bytes used for return address storing + int stackGrowth = -4; + + // Initial state of the frame pointer is SP+stackGrowth. + // TODO: Add tests for `cfi_*` directives + MCCFIInstruction Inst = MCCFIInstruction::cfiDefCfa( + nullptr, MRI.getDwarfRegNum(llvm::M68k::SP, true), -stackGrowth); + MAI->addInitialFrameState(Inst); + + // Add return address to move list + Inst = MCCFIInstruction::createOffset( + nullptr, MRI.getDwarfRegNum(M68k::PC, true), stackGrowth); + MAI->addInitialFrameState(Inst); + + return MAI; +} + +static MCRelocationInfo *createM68kMCRelocationInfo(const Triple &TheTriple, + MCContext &Ctx) { + // Default to the stock relocation info. + return llvm::createMCRelocationInfo(TheTriple, Ctx); +} + +static MCInstPrinter *createM68kMCInstPrinter(const Triple &T, + unsigned SyntaxVariant, + const MCAsmInfo &MAI, + const MCInstrInfo &MII, + const MCRegisterInfo &MRI) { + return new M68kInstPrinter(MAI, MII, MRI); +} + +extern "C" void LLVMInitializeM68kTargetMC() { + Target &T = TheM68kTarget; + + // Register the MC asm info. + RegisterMCAsmInfoFn X(T, createM68kMCAsmInfo); + + // Register the MC instruction info. + TargetRegistry::RegisterMCInstrInfo(T, createM68kMCInstrInfo); + + // Register the MC register info. + TargetRegistry::RegisterMCRegInfo(T, createM68kMCRegisterInfo); + + // Register the MC subtarget info. + TargetRegistry::RegisterMCSubtargetInfo(T, createM68kMCSubtargetInfo); + + // Register the code emitter. + TargetRegistry::RegisterMCCodeEmitter(T, createM68kMCCodeEmitter); + + // Register the MCInstPrinter. + TargetRegistry::RegisterMCInstPrinter(T, createM68kMCInstPrinter); + + // Register the MC relocation info. + TargetRegistry::RegisterMCRelocationInfo(T, createM68kMCRelocationInfo); + + // Register the asm backend. + TargetRegistry::RegisterMCAsmBackend(T, createM68kAsmBackend); +} Index: llvm/lib/Target/M68k/TargetInfo/M68kTargetInfo.cpp =================================================================== --- llvm/lib/Target/M68k/TargetInfo/M68kTargetInfo.cpp +++ llvm/lib/Target/M68k/TargetInfo/M68kTargetInfo.cpp @@ -10,8 +10,14 @@ /// This file contains M68k target initializer. /// //===----------------------------------------------------------------------===// +#include "MCTargetDesc/M68kMCTargetDesc.h" +#include "llvm/Support/TargetRegistry.h" -/// This is just a placeholder to make current -/// commit buildable. Body of this function will -/// be filled in later commits -extern "C" void LLVMInitializeM68kTargetInfo() {} +using namespace llvm; + +Target llvm::TheM68kTarget; + +extern "C" void LLVMInitializeM68kTargetInfo() { + RegisterTarget X( + TheM68kTarget, "m68k", "Motorola 68000 family", "M68k"); +}