//===-- 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(); // NOTE there will be some relaxations for PCD and ARD mem for x20 return Op; } 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) { const Triple &TheTriple = STI.getTargetTriple(); uint8_t OSABI = MCELFObjectTargetWriter::getOSABI(TheTriple.getOS()); return new M68kELFAsmBackend(T, OSABI); }