Path: blob/main/contrib/llvm-project/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.cpp
35294 views
//===-- LoongArchAsmBackend.cpp - LoongArch Assembler Backend -*- C++ -*---===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This file implements the LoongArchAsmBackend class.9//10//===----------------------------------------------------------------------===//1112#include "LoongArchAsmBackend.h"13#include "LoongArchFixupKinds.h"14#include "llvm/MC/MCAsmInfo.h"15#include "llvm/MC/MCAssembler.h"16#include "llvm/MC/MCContext.h"17#include "llvm/MC/MCELFObjectWriter.h"18#include "llvm/MC/MCExpr.h"19#include "llvm/MC/MCSection.h"20#include "llvm/MC/MCValue.h"21#include "llvm/Support/EndianStream.h"22#include "llvm/Support/LEB128.h"23#include "llvm/Support/MathExtras.h"2425#define DEBUG_TYPE "loongarch-asmbackend"2627using namespace llvm;2829std::optional<MCFixupKind>30LoongArchAsmBackend::getFixupKind(StringRef Name) const {31if (STI.getTargetTriple().isOSBinFormatELF()) {32auto Type = llvm::StringSwitch<unsigned>(Name)33#define ELF_RELOC(X, Y) .Case(#X, Y)34#include "llvm/BinaryFormat/ELFRelocs/LoongArch.def"35#undef ELF_RELOC36.Case("BFD_RELOC_NONE", ELF::R_LARCH_NONE)37.Case("BFD_RELOC_32", ELF::R_LARCH_32)38.Case("BFD_RELOC_64", ELF::R_LARCH_64)39.Default(-1u);40if (Type != -1u)41return static_cast<MCFixupKind>(FirstLiteralRelocationKind + Type);42}43return std::nullopt;44}4546const MCFixupKindInfo &47LoongArchAsmBackend::getFixupKindInfo(MCFixupKind Kind) const {48const static MCFixupKindInfo Infos[] = {49// This table *must* be in the order that the fixup_* kinds are defined in50// LoongArchFixupKinds.h.51//52// {name, offset, bits, flags}53{"fixup_loongarch_b16", 10, 16, MCFixupKindInfo::FKF_IsPCRel},54{"fixup_loongarch_b21", 0, 26, MCFixupKindInfo::FKF_IsPCRel},55{"fixup_loongarch_b26", 0, 26, MCFixupKindInfo::FKF_IsPCRel},56{"fixup_loongarch_abs_hi20", 5, 20, 0},57{"fixup_loongarch_abs_lo12", 10, 12, 0},58{"fixup_loongarch_abs64_lo20", 5, 20, 0},59{"fixup_loongarch_abs64_hi12", 10, 12, 0},60{"fixup_loongarch_tls_le_hi20", 5, 20, 0},61{"fixup_loongarch_tls_le_lo12", 10, 12, 0},62{"fixup_loongarch_tls_le64_lo20", 5, 20, 0},63{"fixup_loongarch_tls_le64_hi12", 10, 12, 0},64// TODO: Add more fixup kinds.65};6667static_assert((std::size(Infos)) == LoongArch::NumTargetFixupKinds,68"Not all fixup kinds added to Infos array");6970// Fixup kinds from .reloc directive are like R_LARCH_NONE. They71// do not require any extra processing.72if (Kind >= FirstLiteralRelocationKind)73return MCAsmBackend::getFixupKindInfo(FK_NONE);7475if (Kind < FirstTargetFixupKind)76return MCAsmBackend::getFixupKindInfo(Kind);7778assert(unsigned(Kind - FirstTargetFixupKind) < getNumFixupKinds() &&79"Invalid kind!");80return Infos[Kind - FirstTargetFixupKind];81}8283static void reportOutOfRangeError(MCContext &Ctx, SMLoc Loc, unsigned N) {84Ctx.reportError(Loc, "fixup value out of range [" + Twine(llvm::minIntN(N)) +85", " + Twine(llvm::maxIntN(N)) + "]");86}8788static uint64_t adjustFixupValue(const MCFixup &Fixup, uint64_t Value,89MCContext &Ctx) {90switch (Fixup.getTargetKind()) {91default:92llvm_unreachable("Unknown fixup kind");93case FK_Data_1:94case FK_Data_2:95case FK_Data_4:96case FK_Data_8:97case FK_Data_leb128:98return Value;99case LoongArch::fixup_loongarch_b16: {100if (!isInt<18>(Value))101reportOutOfRangeError(Ctx, Fixup.getLoc(), 18);102if (Value % 4)103Ctx.reportError(Fixup.getLoc(), "fixup value must be 4-byte aligned");104return (Value >> 2) & 0xffff;105}106case LoongArch::fixup_loongarch_b21: {107if (!isInt<23>(Value))108reportOutOfRangeError(Ctx, Fixup.getLoc(), 23);109if (Value % 4)110Ctx.reportError(Fixup.getLoc(), "fixup value must be 4-byte aligned");111return ((Value & 0x3fffc) << 8) | ((Value >> 18) & 0x1f);112}113case LoongArch::fixup_loongarch_b26: {114if (!isInt<28>(Value))115reportOutOfRangeError(Ctx, Fixup.getLoc(), 28);116if (Value % 4)117Ctx.reportError(Fixup.getLoc(), "fixup value must be 4-byte aligned");118return ((Value & 0x3fffc) << 8) | ((Value >> 18) & 0x3ff);119}120case LoongArch::fixup_loongarch_abs_hi20:121case LoongArch::fixup_loongarch_tls_le_hi20:122return (Value >> 12) & 0xfffff;123case LoongArch::fixup_loongarch_abs_lo12:124case LoongArch::fixup_loongarch_tls_le_lo12:125return Value & 0xfff;126case LoongArch::fixup_loongarch_abs64_lo20:127case LoongArch::fixup_loongarch_tls_le64_lo20:128return (Value >> 32) & 0xfffff;129case LoongArch::fixup_loongarch_abs64_hi12:130case LoongArch::fixup_loongarch_tls_le64_hi12:131return (Value >> 52) & 0xfff;132}133}134135static void fixupLeb128(MCContext &Ctx, const MCFixup &Fixup,136MutableArrayRef<char> Data, uint64_t Value) {137unsigned I;138for (I = 0; I != Data.size() && Value; ++I, Value >>= 7)139Data[I] |= uint8_t(Value & 0x7f);140if (Value)141Ctx.reportError(Fixup.getLoc(), "Invalid uleb128 value!");142}143144void LoongArchAsmBackend::applyFixup(const MCAssembler &Asm,145const MCFixup &Fixup,146const MCValue &Target,147MutableArrayRef<char> Data, uint64_t Value,148bool IsResolved,149const MCSubtargetInfo *STI) const {150if (!Value)151return; // Doesn't change encoding.152153MCFixupKind Kind = Fixup.getKind();154if (Kind >= FirstLiteralRelocationKind)155return;156MCFixupKindInfo Info = getFixupKindInfo(Kind);157MCContext &Ctx = Asm.getContext();158159// Fixup leb128 separately.160if (Fixup.getTargetKind() == FK_Data_leb128)161return fixupLeb128(Ctx, Fixup, Data, Value);162163// Apply any target-specific value adjustments.164Value = adjustFixupValue(Fixup, Value, Ctx);165166// Shift the value into position.167Value <<= Info.TargetOffset;168169unsigned Offset = Fixup.getOffset();170unsigned NumBytes = alignTo(Info.TargetSize + Info.TargetOffset, 8) / 8;171172assert(Offset + NumBytes <= Data.size() && "Invalid fixup offset!");173// For each byte of the fragment that the fixup touches, mask in the174// bits from the fixup value.175for (unsigned I = 0; I != NumBytes; ++I) {176Data[Offset + I] |= uint8_t((Value >> (I * 8)) & 0xff);177}178}179180// Linker relaxation may change code size. We have to insert Nops181// for .align directive when linker relaxation enabled. So then Linker182// could satisfy alignment by removing Nops.183// The function returns the total Nops Size we need to insert.184bool LoongArchAsmBackend::shouldInsertExtraNopBytesForCodeAlign(185const MCAlignFragment &AF, unsigned &Size) {186// Calculate Nops Size only when linker relaxation enabled.187if (!AF.getSubtargetInfo()->hasFeature(LoongArch::FeatureRelax))188return false;189190// Ignore alignment if MaxBytesToEmit is less than the minimum Nop size.191const unsigned MinNopLen = 4;192if (AF.getMaxBytesToEmit() < MinNopLen)193return false;194Size = AF.getAlignment().value() - MinNopLen;195return AF.getAlignment() > MinNopLen;196}197198// We need to insert R_LARCH_ALIGN relocation type to indicate the199// position of Nops and the total bytes of the Nops have been inserted200// when linker relaxation enabled.201// The function inserts fixup_loongarch_align fixup which eventually will202// transfer to R_LARCH_ALIGN relocation type.203// The improved R_LARCH_ALIGN requires symbol index. The lowest 8 bits of204// addend represent alignment and the other bits of addend represent the205// maximum number of bytes to emit. The maximum number of bytes is zero206// means ignore the emit limit.207bool LoongArchAsmBackend::shouldInsertFixupForCodeAlign(MCAssembler &Asm,208MCAlignFragment &AF) {209// Insert the fixup only when linker relaxation enabled.210if (!AF.getSubtargetInfo()->hasFeature(LoongArch::FeatureRelax))211return false;212213// Calculate total Nops we need to insert. If there are none to insert214// then simply return.215unsigned InsertedNopBytes;216if (!shouldInsertExtraNopBytesForCodeAlign(AF, InsertedNopBytes))217return false;218219MCSection *Sec = AF.getParent();220MCContext &Ctx = Asm.getContext();221const MCExpr *Dummy = MCConstantExpr::create(0, Ctx);222// Create fixup_loongarch_align fixup.223MCFixup Fixup =224MCFixup::create(0, Dummy, MCFixupKind(LoongArch::fixup_loongarch_align));225unsigned MaxBytesToEmit = AF.getMaxBytesToEmit();226227auto createExtendedValue = [&]() {228const MCSymbolRefExpr *MCSym = getSecToAlignSym()[Sec];229if (MCSym == nullptr) {230// Define a marker symbol at the section with an offset of 0.231MCSymbol *Sym = Ctx.createNamedTempSymbol("la-relax-align");232Sym->setFragment(&*Sec->getBeginSymbol()->getFragment());233Asm.registerSymbol(*Sym);234MCSym = MCSymbolRefExpr::create(Sym, Ctx);235getSecToAlignSym()[Sec] = MCSym;236}237return MCValue::get(MCSym, nullptr,238MaxBytesToEmit << 8 | Log2(AF.getAlignment()));239};240241uint64_t FixedValue = 0;242MCValue Value = MaxBytesToEmit >= InsertedNopBytes243? MCValue::get(InsertedNopBytes)244: createExtendedValue();245Asm.getWriter().recordRelocation(Asm, &AF, Fixup, Value, FixedValue);246247return true;248}249250bool LoongArchAsmBackend::shouldForceRelocation(const MCAssembler &Asm,251const MCFixup &Fixup,252const MCValue &Target,253const MCSubtargetInfo *STI) {254if (Fixup.getKind() >= FirstLiteralRelocationKind)255return true;256switch (Fixup.getTargetKind()) {257default:258return STI->hasFeature(LoongArch::FeatureRelax);259case FK_Data_1:260case FK_Data_2:261case FK_Data_4:262case FK_Data_8:263case FK_Data_leb128:264return !Target.isAbsolute();265}266}267268static inline std::pair<MCFixupKind, MCFixupKind>269getRelocPairForSize(unsigned Size) {270switch (Size) {271default:272llvm_unreachable("unsupported fixup size");273case 6:274return std::make_pair(275MCFixupKind(FirstLiteralRelocationKind + ELF::R_LARCH_ADD6),276MCFixupKind(FirstLiteralRelocationKind + ELF::R_LARCH_SUB6));277case 8:278return std::make_pair(279MCFixupKind(FirstLiteralRelocationKind + ELF::R_LARCH_ADD8),280MCFixupKind(FirstLiteralRelocationKind + ELF::R_LARCH_SUB8));281case 16:282return std::make_pair(283MCFixupKind(FirstLiteralRelocationKind + ELF::R_LARCH_ADD16),284MCFixupKind(FirstLiteralRelocationKind + ELF::R_LARCH_SUB16));285case 32:286return std::make_pair(287MCFixupKind(FirstLiteralRelocationKind + ELF::R_LARCH_ADD32),288MCFixupKind(FirstLiteralRelocationKind + ELF::R_LARCH_SUB32));289case 64:290return std::make_pair(291MCFixupKind(FirstLiteralRelocationKind + ELF::R_LARCH_ADD64),292MCFixupKind(FirstLiteralRelocationKind + ELF::R_LARCH_SUB64));293case 128:294return std::make_pair(295MCFixupKind(FirstLiteralRelocationKind + ELF::R_LARCH_ADD_ULEB128),296MCFixupKind(FirstLiteralRelocationKind + ELF::R_LARCH_SUB_ULEB128));297}298}299300std::pair<bool, bool> LoongArchAsmBackend::relaxLEB128(const MCAssembler &Asm,301MCLEBFragment &LF,302int64_t &Value) const {303const MCExpr &Expr = LF.getValue();304if (LF.isSigned() || !Expr.evaluateKnownAbsolute(Value, Asm))305return std::make_pair(false, false);306LF.getFixups().push_back(307MCFixup::create(0, &Expr, FK_Data_leb128, Expr.getLoc()));308return std::make_pair(true, true);309}310311bool LoongArchAsmBackend::relaxDwarfLineAddr(const MCAssembler &Asm,312MCDwarfLineAddrFragment &DF,313bool &WasRelaxed) const {314MCContext &C = Asm.getContext();315316int64_t LineDelta = DF.getLineDelta();317const MCExpr &AddrDelta = DF.getAddrDelta();318SmallVectorImpl<char> &Data = DF.getContents();319SmallVectorImpl<MCFixup> &Fixups = DF.getFixups();320size_t OldSize = Data.size();321322int64_t Value;323if (AddrDelta.evaluateAsAbsolute(Value, Asm))324return false;325bool IsAbsolute = AddrDelta.evaluateKnownAbsolute(Value, Asm);326assert(IsAbsolute && "CFA with invalid expression");327(void)IsAbsolute;328329Data.clear();330Fixups.clear();331raw_svector_ostream OS(Data);332333// INT64_MAX is a signal that this is actually a DW_LNE_end_sequence.334if (LineDelta != INT64_MAX) {335OS << uint8_t(dwarf::DW_LNS_advance_line);336encodeSLEB128(LineDelta, OS);337}338339unsigned Offset;340std::pair<MCFixupKind, MCFixupKind> FK;341342// According to the DWARF specification, the `DW_LNS_fixed_advance_pc` opcode343// takes a single unsigned half (unencoded) operand. The maximum encodable344// value is therefore 65535. Set a conservative upper bound for relaxation.345if (Value > 60000) {346unsigned PtrSize = C.getAsmInfo()->getCodePointerSize();347348OS << uint8_t(dwarf::DW_LNS_extended_op);349encodeULEB128(PtrSize + 1, OS);350351OS << uint8_t(dwarf::DW_LNE_set_address);352Offset = OS.tell();353assert((PtrSize == 4 || PtrSize == 8) && "Unexpected pointer size");354FK = getRelocPairForSize(PtrSize == 4 ? 32 : 64);355OS.write_zeros(PtrSize);356} else {357OS << uint8_t(dwarf::DW_LNS_fixed_advance_pc);358Offset = OS.tell();359FK = getRelocPairForSize(16);360support::endian::write<uint16_t>(OS, 0, llvm::endianness::little);361}362363const MCBinaryExpr &MBE = cast<MCBinaryExpr>(AddrDelta);364Fixups.push_back(MCFixup::create(Offset, MBE.getLHS(), std::get<0>(FK)));365Fixups.push_back(MCFixup::create(Offset, MBE.getRHS(), std::get<1>(FK)));366367if (LineDelta == INT64_MAX) {368OS << uint8_t(dwarf::DW_LNS_extended_op);369OS << uint8_t(1);370OS << uint8_t(dwarf::DW_LNE_end_sequence);371} else {372OS << uint8_t(dwarf::DW_LNS_copy);373}374375WasRelaxed = OldSize != Data.size();376return true;377}378379bool LoongArchAsmBackend::relaxDwarfCFA(const MCAssembler &Asm,380MCDwarfCallFrameFragment &DF,381bool &WasRelaxed) const {382const MCExpr &AddrDelta = DF.getAddrDelta();383SmallVectorImpl<char> &Data = DF.getContents();384SmallVectorImpl<MCFixup> &Fixups = DF.getFixups();385size_t OldSize = Data.size();386387int64_t Value;388if (AddrDelta.evaluateAsAbsolute(Value, Asm))389return false;390bool IsAbsolute = AddrDelta.evaluateKnownAbsolute(Value, Asm);391assert(IsAbsolute && "CFA with invalid expression");392(void)IsAbsolute;393394Data.clear();395Fixups.clear();396raw_svector_ostream OS(Data);397398assert(Asm.getContext().getAsmInfo()->getMinInstAlignment() == 1 &&399"expected 1-byte alignment");400if (Value == 0) {401WasRelaxed = OldSize != Data.size();402return true;403}404405auto AddFixups = [&Fixups,406&AddrDelta](unsigned Offset,407std::pair<MCFixupKind, MCFixupKind> FK) {408const MCBinaryExpr &MBE = cast<MCBinaryExpr>(AddrDelta);409Fixups.push_back(MCFixup::create(Offset, MBE.getLHS(), std::get<0>(FK)));410Fixups.push_back(MCFixup::create(Offset, MBE.getRHS(), std::get<1>(FK)));411};412413if (isUIntN(6, Value)) {414OS << uint8_t(dwarf::DW_CFA_advance_loc);415AddFixups(0, getRelocPairForSize(6));416} else if (isUInt<8>(Value)) {417OS << uint8_t(dwarf::DW_CFA_advance_loc1);418support::endian::write<uint8_t>(OS, 0, llvm::endianness::little);419AddFixups(1, getRelocPairForSize(8));420} else if (isUInt<16>(Value)) {421OS << uint8_t(dwarf::DW_CFA_advance_loc2);422support::endian::write<uint16_t>(OS, 0, llvm::endianness::little);423AddFixups(1, getRelocPairForSize(16));424} else if (isUInt<32>(Value)) {425OS << uint8_t(dwarf::DW_CFA_advance_loc4);426support::endian::write<uint32_t>(OS, 0, llvm::endianness::little);427AddFixups(1, getRelocPairForSize(32));428} else {429llvm_unreachable("unsupported CFA encoding");430}431432WasRelaxed = OldSize != Data.size();433return true;434}435436bool LoongArchAsmBackend::writeNopData(raw_ostream &OS, uint64_t Count,437const MCSubtargetInfo *STI) const {438// We mostly follow binutils' convention here: align to 4-byte boundary with a439// 0-fill padding.440OS.write_zeros(Count % 4);441442// The remainder is now padded with 4-byte nops.443// nop: andi r0, r0, 0444for (; Count >= 4; Count -= 4)445OS.write("\0\0\x40\x03", 4);446447return true;448}449450bool LoongArchAsmBackend::handleAddSubRelocations(const MCAssembler &Asm,451const MCFragment &F,452const MCFixup &Fixup,453const MCValue &Target,454uint64_t &FixedValue) const {455std::pair<MCFixupKind, MCFixupKind> FK;456uint64_t FixedValueA, FixedValueB;457const MCSymbol &SA = Target.getSymA()->getSymbol();458const MCSymbol &SB = Target.getSymB()->getSymbol();459460bool force = !SA.isInSection() || !SB.isInSection();461if (!force) {462const MCSection &SecA = SA.getSection();463const MCSection &SecB = SB.getSection();464465// We need record relocation if SecA != SecB. Usually SecB is same as the466// section of Fixup, which will be record the relocation as PCRel. If SecB467// is not same as the section of Fixup, it will report error. Just return468// false and then this work can be finished by handleFixup.469if (&SecA != &SecB)470return false;471472// In SecA == SecB case. If the linker relaxation is enabled, we need record473// the ADD, SUB relocations. Otherwise the FixedValue has already been calc-474// ulated out in evaluateFixup, return true and avoid record relocations.475if (!STI.hasFeature(LoongArch::FeatureRelax))476return true;477}478479switch (Fixup.getKind()) {480case llvm::FK_Data_1:481FK = getRelocPairForSize(8);482break;483case llvm::FK_Data_2:484FK = getRelocPairForSize(16);485break;486case llvm::FK_Data_4:487FK = getRelocPairForSize(32);488break;489case llvm::FK_Data_8:490FK = getRelocPairForSize(64);491break;492case llvm::FK_Data_leb128:493FK = getRelocPairForSize(128);494break;495default:496llvm_unreachable("unsupported fixup size");497}498MCValue A = MCValue::get(Target.getSymA(), nullptr, Target.getConstant());499MCValue B = MCValue::get(Target.getSymB());500auto FA = MCFixup::create(Fixup.getOffset(), nullptr, std::get<0>(FK));501auto FB = MCFixup::create(Fixup.getOffset(), nullptr, std::get<1>(FK));502auto &Assembler = const_cast<MCAssembler &>(Asm);503Asm.getWriter().recordRelocation(Assembler, &F, FA, A, FixedValueA);504Asm.getWriter().recordRelocation(Assembler, &F, FB, B, FixedValueB);505FixedValue = FixedValueA - FixedValueB;506return true;507}508509std::unique_ptr<MCObjectTargetWriter>510LoongArchAsmBackend::createObjectTargetWriter() const {511return createLoongArchELFObjectWriter(512OSABI, Is64Bit, STI.hasFeature(LoongArch::FeatureRelax));513}514515MCAsmBackend *llvm::createLoongArchAsmBackend(const Target &T,516const MCSubtargetInfo &STI,517const MCRegisterInfo &MRI,518const MCTargetOptions &Options) {519const Triple &TT = STI.getTargetTriple();520uint8_t OSABI = MCELFObjectTargetWriter::getOSABI(TT.getOS());521return new LoongArchAsmBackend(STI, OSABI, TT.isArch64Bit(), Options);522}523524525