| //===-- 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<bool>(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<MCOperand> 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<int64_t>(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<int64_t>(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<MCOperand>, |
| 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<int64_t>(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<MCObjectTargetWriter> |
| 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); |
| } |