//===-- SparcAsmBackend.cpp - Sparc Assembler Backend ---------------------===// // // 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 // //===----------------------------------------------------------------------===// #include "MCTargetDesc/SparcFixupKinds.h" #include "MCTargetDesc/SparcMCTargetDesc.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/MC/MCAsmBackend.h" #include "llvm/MC/MCELFObjectWriter.h" #include "llvm/MC/MCExpr.h" #include "llvm/MC/MCObjectWriter.h" #include "llvm/MC/MCSubtargetInfo.h" #include "llvm/MC/MCValue.h" #include "llvm/MC/TargetRegistry.h" #include "llvm/Support/EndianStream.h" using namespace llvm; static unsigned adjustFixupValue(unsigned Kind, uint64_t Value) { switch (Kind) { default: assert(uint16_t(Kind) < FirstTargetFixupKind && "Unknown fixup kind!"); return Value; case FK_Data_1: case FK_Data_2: case FK_Data_4: case FK_Data_8: return Value; case Sparc::fixup_sparc_call30: return (Value >> 2) & 0x3fffffff; case ELF::R_SPARC_WDISP22: return (Value >> 2) & 0x3fffff; case ELF::R_SPARC_WDISP19: return (Value >> 2) & 0x7ffff; case ELF::R_SPARC_WDISP16: { // A.3 Branch on Integer Register with Prediction (BPr) // Inst{21-20} = d16hi; // Inst{13-0} = d16lo; unsigned d16hi = (Value >> 16) & 0x3; unsigned d16lo = (Value >> 2) & 0x3fff; return (d16hi << 20) | d16lo; } case ELF::R_SPARC_WDISP10: { // FIXME this really should be an error reporting check. assert((Value & 0x3) == 0); // 7.17 Compare and Branch // Inst{20-19} = d10hi; // Inst{12-5} = d10lo; unsigned d10hi = (Value >> 10) & 0x3; unsigned d10lo = (Value >> 2) & 0xff; return (d10hi << 19) | (d10lo << 5); } case ELF::R_SPARC_HIX22: return (~Value >> 10) & 0x3fffff; case ELF::R_SPARC_PC22: case ELF::R_SPARC_HI22: case ELF::R_SPARC_LM22: return (Value >> 10) & 0x3fffff; case Sparc::fixup_sparc_13: return Value & 0x1fff; case ELF::R_SPARC_5: return Value & 0x1f; case ELF::R_SPARC_LOX10: return (Value & 0x3ff) | 0x1c00; case ELF::R_SPARC_PC10: case ELF::R_SPARC_LO10: return Value & 0x3ff; case ELF::R_SPARC_H44: return (Value >> 22) & 0x3fffff; case ELF::R_SPARC_M44: return (Value >> 12) & 0x3ff; case ELF::R_SPARC_L44: return Value & 0xfff; case ELF::R_SPARC_HH22: return (Value >> 42) & 0x3fffff; case ELF::R_SPARC_HM10: return (Value >> 32) & 0x3ff; } } /// getFixupKindNumBytes - The number of bytes the fixup may change. static unsigned getFixupKindNumBytes(unsigned Kind) { switch (Kind) { default: return 4; case FK_Data_1: return 1; case FK_Data_2: return 2; case FK_Data_8: return 8; } } namespace { class SparcAsmBackend : public MCAsmBackend { protected: bool Is64Bit; bool IsV8Plus; public: SparcAsmBackend(const MCSubtargetInfo &STI) : MCAsmBackend(STI.getTargetTriple().isLittleEndian() ? llvm::endianness::little : llvm::endianness::big), Is64Bit(STI.getTargetTriple().isArch64Bit()), IsV8Plus(STI.hasFeature(Sparc::FeatureV8Plus)) {} std::optional getFixupKind(StringRef Name) const override; MCFixupKindInfo getFixupKindInfo(MCFixupKind Kind) const override; void applyFixup(const MCFragment &, const MCFixup &, const MCValue &Target, uint8_t *Data, uint64_t Value, bool IsResolved) override; bool writeNopData(raw_ostream &OS, uint64_t Count, const MCSubtargetInfo *STI) const override { // If the count is not 4-byte aligned, we must be writing data into the // text section (otherwise we have unaligned instructions, and thus have // far bigger problems), so just write zeros instead. OS.write_zeros(Count % 4); uint64_t NumNops = Count / 4; for (uint64_t i = 0; i != NumNops; ++i) support::endian::write(OS, 0x01000000, Endian); return true; } }; class ELFSparcAsmBackend : public SparcAsmBackend { Triple::OSType OSType; public: ELFSparcAsmBackend(const MCSubtargetInfo &STI, Triple::OSType OSType) : SparcAsmBackend(STI), OSType(OSType) {} std::unique_ptr createObjectTargetWriter() const override { uint8_t OSABI = MCELFObjectTargetWriter::getOSABI(OSType); return createSparcELFObjectWriter(Is64Bit, IsV8Plus, OSABI); } }; } // end anonymous namespace std::optional SparcAsmBackend::getFixupKind(StringRef Name) const { unsigned Type; Type = llvm::StringSwitch(Name) #define ELF_RELOC(X, Y) .Case(#X, Y) #include "llvm/BinaryFormat/ELFRelocs/Sparc.def" #undef ELF_RELOC .Case("BFD_RELOC_NONE", ELF::R_SPARC_NONE) .Case("BFD_RELOC_8", ELF::R_SPARC_8) .Case("BFD_RELOC_16", ELF::R_SPARC_16) .Case("BFD_RELOC_32", ELF::R_SPARC_32) .Case("BFD_RELOC_64", ELF::R_SPARC_64) .Default(-1u); if (Type == -1u) return std::nullopt; return static_cast(FirstLiteralRelocationKind + Type); } MCFixupKindInfo SparcAsmBackend::getFixupKindInfo(MCFixupKind Kind) const { // clang-format off const static MCFixupKindInfo InfosBE[Sparc::NumTargetFixupKinds] = { // name offset bits flags {"fixup_sparc_call30", 2, 30, 0}, {"fixup_sparc_13", 19, 13, 0}, }; const static MCFixupKindInfo InfosLE[Sparc::NumTargetFixupKinds] = { // name offset bits flags {"fixup_sparc_call30", 0, 30, 0}, {"fixup_sparc_13", 0, 13, 0}, }; // clang-format on if (!mc::isRelocation(Kind)) { if (Kind < FirstTargetFixupKind) return MCAsmBackend::getFixupKindInfo(Kind); assert(unsigned(Kind - FirstTargetFixupKind) < Sparc::NumTargetFixupKinds && "Invalid kind!"); if (Endian == llvm::endianness::little) return InfosLE[Kind - FirstTargetFixupKind]; return InfosBE[Kind - FirstTargetFixupKind]; } MCFixupKindInfo Info{}; switch (uint16_t(Kind)) { case ELF::R_SPARC_PC10: Info = {"", 22, 10, 0}; break; case ELF::R_SPARC_PC22: Info = {"", 10, 22, 0}; break; case ELF::R_SPARC_WDISP10: Info = {"", 0, 32, 0}; break; case ELF::R_SPARC_WDISP16: Info = {"", 0, 32, 0}; break; case ELF::R_SPARC_WDISP19: Info = {"", 13, 19, 0}; break; case ELF::R_SPARC_WDISP22: Info = {"", 10, 22, 0}; break; case ELF::R_SPARC_HI22: Info = {"", 10, 22, 0}; break; case ELF::R_SPARC_LO10: Info = {"", 22, 10, 0}; break; case ELF::R_SPARC_HH22: Info = {"", 10, 22, 0}; break; case ELF::R_SPARC_HM10: Info = {"", 22, 10, 0}; break; case ELF::R_SPARC_LM22: Info = {"", 10, 22, 0}; break; case ELF::R_SPARC_HIX22: Info = {"", 10, 22, 0}; break; case ELF::R_SPARC_LOX10: Info = {"", 19, 13, 0}; break; } if (Endian == llvm::endianness::little) Info.TargetOffset = 32 - Info.TargetOffset - Info.TargetSize; return Info; } void SparcAsmBackend::applyFixup(const MCFragment &F, const MCFixup &Fixup, const MCValue &Target, uint8_t *Data, uint64_t Value, bool IsResolved) { maybeAddReloc(F, Fixup, Target, Value, IsResolved); if (!IsResolved) return; Value = adjustFixupValue(Fixup.getKind(), Value); unsigned NumBytes = getFixupKindNumBytes(Fixup.getKind()); // For each byte of the fragment that the fixup touches, mask in the // bits from the fixup value. for (unsigned i = 0; i != NumBytes; ++i) { unsigned Idx = Endian == llvm::endianness::little ? i : (NumBytes - 1) - i; Data[Idx] |= uint8_t((Value >> (i * 8)) & 0xff); } } MCAsmBackend *llvm::createSparcAsmBackend(const Target &T, const MCSubtargetInfo &STI, const MCRegisterInfo &MRI, const MCTargetOptions &Options) { return new ELFSparcAsmBackend(STI, STI.getTargetTriple().getOS()); }