//===-- 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/MCAssembler.h" #include "llvm/MC/MCELFObjectWriter.h" #include "llvm/MC/MCExpr.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/MC/MCValue.h" #include "llvm/MC/TargetRegistry.h" #include "llvm/Support/Debug.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/MathExtras.h" #include "llvm/Support/raw_ostream.h" using namespace llvm; #define DEBUG_TYPE "M68k-asm-backend" namespace { class M68kAsmBackend : public MCAsmBackend { bool Allows32BitBranch; public: M68kAsmBackend(const Target &T, const MCSubtargetInfo &STI) : MCAsmBackend(llvm::endianness::big), Allows32BitBranch(llvm::StringSwitch(STI.getCPU()) .CasesLower("m68020", "m68030", "m68040", true) .Default(false)) {} void applyFixup(const MCFragment &, const MCFixup &, const MCValue &, uint8_t *Data, uint64_t Value, bool IsResolved) override; bool mayNeedRelaxation(unsigned Opcode, ArrayRef Operands, const MCSubtargetInfo &STI) const override; bool fixupNeedsRelaxation(const MCFixup &Fixup, uint64_t Value) 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 MCSubtargetInfo *STI) const override; }; } // end anonymous namespace void M68kAsmBackend::applyFixup(const MCFragment &F, const MCFixup &Fixup, const MCValue &Target, uint8_t *Data, uint64_t Value, bool IsResolved) { if (!IsResolved) Asm->getWriter().recordRelocation(F, Fixup, Target, Value); unsigned Size = 1 << getFixupKindLog2Size(Fixup.getKind()); assert(Fixup.getOffset() + Size <= F.getSize() && "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, static_cast(Value)) && "Value does not fit in the Fixup field"); // Write in Big Endian for (unsigned i = 0; i != Size; ++i) Data[i] = uint8_t(static_cast(Value) >> ((Size - i - 1) * 8)); } /// 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(unsigned Op) { switch (Op) { default: return Op; // 8 -> 16 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; // 16 -> 32 case M68k::BRA16: return M68k::BRA32; case M68k::Bcc16: return M68k::Bcc32; case M68k::Bls16: return M68k::Bls32; case M68k::Blt16: return M68k::Blt32; case M68k::Beq16: return M68k::Beq32; case M68k::Bmi16: return M68k::Bmi32; case M68k::Bne16: return M68k::Bne32; case M68k::Bge16: return M68k::Bge32; case M68k::Bcs16: return M68k::Bcs32; case M68k::Bpl16: return M68k::Bpl32; case M68k::Bgt16: return M68k::Bgt32; case M68k::Bhi16: return M68k::Bhi32; case M68k::Bvc16: return M68k::Bvc32; case M68k::Ble16: return M68k::Ble32; case M68k::Bvs16: return M68k::Bvs32; } } static unsigned getRelaxedOpcode(unsigned Opcode) { // NOTE there will be some relaxations for PCD and ARD mem for x20 return getRelaxedOpcodeBranch(Opcode); } bool M68kAsmBackend::mayNeedRelaxation(unsigned Opcode, ArrayRef, const MCSubtargetInfo &STI) const { // Branches can always be relaxed in either mode. return getRelaxedOpcode(Opcode) != Opcode; // NOTE will change for x20 mem } bool M68kAsmBackend::fixupNeedsRelaxation(const MCFixup &Fixup, uint64_t UnsignedValue) const { int64_t Value = static_cast(UnsignedValue); if (!isInt<32>(Value) || (!Allows32BitBranch && !isInt<16>(Value))) llvm_unreachable("Cannot relax the instruction, value does not fit"); // Relax if the value is too big for a (signed) i8 // (or signed i16 if 32 bit branches can be used). This means // that byte-wide instructions have to matched by default unsigned KindLog2Size = getFixupKindLog2Size(Fixup.getKind()); bool FixupFieldTooSmall = false; if (!isInt<8>(Value) && KindLog2Size == 0) FixupFieldTooSmall = true; else if (!isInt<16>(Value) && KindLog2Size <= 1) FixupFieldTooSmall = true; // 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). bool ZeroDisplacementNeedsFixup = Value == 0 && KindLog2Size == 0; return ZeroDisplacementNeedsFixup || FixupFieldTooSmall; } // 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 { unsigned RelaxedOp = getRelaxedOpcode(Inst.getOpcode()); assert(RelaxedOp != Inst.getOpcode()); Inst.setOpcode(RelaxedOp); } bool M68kAsmBackend::writeNopData(raw_ostream &OS, uint64_t Count, const MCSubtargetInfo *STI) 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, const MCSubtargetInfo &STI, uint8_t OSABI) : M68kAsmBackend(T, STI), 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) { const Triple &TheTriple = STI.getTargetTriple(); uint8_t OSABI = MCELFObjectTargetWriter::getOSABI(TheTriple.getOS()); return new M68kELFAsmBackend(T, STI, OSABI); }