500 lines · cpp
1//===-- LoongArchAsmBackend.cpp - LoongArch Assembler Backend -*- C++ -*---===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// This file implements the LoongArchAsmBackend class.10//11//===----------------------------------------------------------------------===//12 13#include "LoongArchAsmBackend.h"14#include "LoongArchFixupKinds.h"15#include "llvm/BinaryFormat/ELF.h"16#include "llvm/MC/MCAsmInfo.h"17#include "llvm/MC/MCAssembler.h"18#include "llvm/MC/MCContext.h"19#include "llvm/MC/MCELFObjectWriter.h"20#include "llvm/MC/MCExpr.h"21#include "llvm/MC/MCSection.h"22#include "llvm/MC/MCValue.h"23#include "llvm/Support/EndianStream.h"24#include "llvm/Support/LEB128.h"25#include "llvm/Support/MathExtras.h"26 27#define DEBUG_TYPE "loongarch-asmbackend"28 29using namespace llvm;30 31LoongArchAsmBackend::LoongArchAsmBackend(const MCSubtargetInfo &STI,32 uint8_t OSABI, bool Is64Bit,33 const MCTargetOptions &Options)34 : MCAsmBackend(llvm::endianness::little), STI(STI), OSABI(OSABI),35 Is64Bit(Is64Bit), TargetOptions(Options) {}36 37std::optional<MCFixupKind>38LoongArchAsmBackend::getFixupKind(StringRef Name) const {39 if (STI.getTargetTriple().isOSBinFormatELF()) {40 auto Type = llvm::StringSwitch<unsigned>(Name)41#define ELF_RELOC(X, Y) .Case(#X, Y)42#include "llvm/BinaryFormat/ELFRelocs/LoongArch.def"43#undef ELF_RELOC44 .Case("BFD_RELOC_NONE", ELF::R_LARCH_NONE)45 .Case("BFD_RELOC_32", ELF::R_LARCH_32)46 .Case("BFD_RELOC_64", ELF::R_LARCH_64)47 .Default(-1u);48 if (Type != -1u)49 return static_cast<MCFixupKind>(FirstLiteralRelocationKind + Type);50 }51 return std::nullopt;52}53 54MCFixupKindInfo LoongArchAsmBackend::getFixupKindInfo(MCFixupKind Kind) const {55 const static MCFixupKindInfo Infos[] = {56 // This table *must* be in the order that the fixup_* kinds are defined in57 // LoongArchFixupKinds.h.58 //59 // {name, offset, bits, flags}60 {"fixup_loongarch_b16", 10, 16, 0},61 {"fixup_loongarch_b21", 0, 26, 0},62 {"fixup_loongarch_b26", 0, 26, 0},63 {"fixup_loongarch_abs_hi20", 5, 20, 0},64 {"fixup_loongarch_abs_lo12", 10, 12, 0},65 {"fixup_loongarch_abs64_lo20", 5, 20, 0},66 {"fixup_loongarch_abs64_hi12", 10, 12, 0},67 };68 69 static_assert((std::size(Infos)) == LoongArch::NumTargetFixupKinds,70 "Not all fixup kinds added to Infos array");71 72 // Fixup kinds from .reloc directive are like R_LARCH_NONE. They73 // do not require any extra processing.74 if (mc::isRelocation(Kind))75 return {};76 77 if (Kind < FirstTargetFixupKind)78 return MCAsmBackend::getFixupKindInfo(Kind);79 80 assert(unsigned(Kind - FirstTargetFixupKind) <81 LoongArch::NumTargetFixupKinds &&82 "Invalid kind!");83 return Infos[Kind - FirstTargetFixupKind];84}85 86static void reportOutOfRangeError(MCContext &Ctx, SMLoc Loc, unsigned N) {87 Ctx.reportError(Loc, "fixup value out of range [" + Twine(llvm::minIntN(N)) +88 ", " + Twine(llvm::maxIntN(N)) + "]");89}90 91static uint64_t adjustFixupValue(const MCFixup &Fixup, uint64_t Value,92 MCContext &Ctx) {93 switch (Fixup.getKind()) {94 default:95 llvm_unreachable("Unknown fixup kind");96 case FK_Data_1:97 case FK_Data_2:98 case FK_Data_4:99 case FK_Data_8:100 case FK_Data_leb128:101 return Value;102 case LoongArch::fixup_loongarch_b16: {103 if (!isInt<18>(Value))104 reportOutOfRangeError(Ctx, Fixup.getLoc(), 18);105 if (Value % 4)106 Ctx.reportError(Fixup.getLoc(), "fixup value must be 4-byte aligned");107 return (Value >> 2) & 0xffff;108 }109 case LoongArch::fixup_loongarch_b21: {110 if (!isInt<23>(Value))111 reportOutOfRangeError(Ctx, Fixup.getLoc(), 23);112 if (Value % 4)113 Ctx.reportError(Fixup.getLoc(), "fixup value must be 4-byte aligned");114 return ((Value & 0x3fffc) << 8) | ((Value >> 18) & 0x1f);115 }116 case LoongArch::fixup_loongarch_b26: {117 if (!isInt<28>(Value))118 reportOutOfRangeError(Ctx, Fixup.getLoc(), 28);119 if (Value % 4)120 Ctx.reportError(Fixup.getLoc(), "fixup value must be 4-byte aligned");121 return ((Value & 0x3fffc) << 8) | ((Value >> 18) & 0x3ff);122 }123 case LoongArch::fixup_loongarch_abs_hi20:124 return (Value >> 12) & 0xfffff;125 case LoongArch::fixup_loongarch_abs_lo12:126 return Value & 0xfff;127 case LoongArch::fixup_loongarch_abs64_lo20:128 return (Value >> 32) & 0xfffff;129 case LoongArch::fixup_loongarch_abs64_hi12:130 return (Value >> 52) & 0xfff;131 }132}133 134static void fixupLeb128(MCContext &Ctx, const MCFixup &Fixup, uint8_t *Data,135 uint64_t Value) {136 unsigned I;137 for (I = 0; Value; ++I, Value >>= 7)138 Data[I] |= uint8_t(Value & 0x7f);139 if (Value)140 Ctx.reportError(Fixup.getLoc(), "Invalid uleb128 value!");141}142 143void LoongArchAsmBackend::applyFixup(const MCFragment &F, const MCFixup &Fixup,144 const MCValue &Target, uint8_t *Data,145 uint64_t Value, bool IsResolved) {146 IsResolved = addReloc(F, Fixup, Target, Value, IsResolved);147 if (!Value)148 return; // Doesn't change encoding.149 150 auto Kind = Fixup.getKind();151 if (mc::isRelocation(Kind))152 return;153 MCFixupKindInfo Info = getFixupKindInfo(Kind);154 MCContext &Ctx = getContext();155 156 // Fixup leb128 separately.157 if (Fixup.getKind() == FK_Data_leb128)158 return fixupLeb128(Ctx, Fixup, Data, Value);159 160 // Apply any target-specific value adjustments.161 Value = adjustFixupValue(Fixup, Value, Ctx);162 163 // Shift the value into position.164 Value <<= Info.TargetOffset;165 166 unsigned NumBytes = alignTo(Info.TargetSize + Info.TargetOffset, 8) / 8;167 168 assert(Fixup.getOffset() + NumBytes <= F.getSize() &&169 "Invalid fixup offset!");170 // For each byte of the fragment that the fixup touches, mask in the171 // bits from the fixup value.172 for (unsigned I = 0; I != NumBytes; ++I) {173 Data[I] |= uint8_t((Value >> (I * 8)) & 0xff);174 }175}176 177static inline std::pair<MCFixupKind, MCFixupKind>178getRelocPairForSize(unsigned Size) {179 switch (Size) {180 default:181 llvm_unreachable("unsupported fixup size");182 case 6:183 return std::make_pair(ELF::R_LARCH_ADD6, ELF::R_LARCH_SUB6);184 case 8:185 return std::make_pair(ELF::R_LARCH_ADD8, ELF::R_LARCH_SUB8);186 case 16:187 return std::make_pair(ELF::R_LARCH_ADD16, ELF::R_LARCH_SUB16);188 case 32:189 return std::make_pair(ELF::R_LARCH_ADD32, ELF::R_LARCH_SUB32);190 case 64:191 return std::make_pair(ELF::R_LARCH_ADD64, ELF::R_LARCH_SUB64);192 case 128:193 return std::make_pair(ELF::R_LARCH_ADD_ULEB128, ELF::R_LARCH_SUB_ULEB128);194 }195}196 197// Check if an R_LARCH_ALIGN relocation is needed for an alignment directive.198// If conditions are met, compute the padding size and create a fixup encoding199// the padding size in the addend. If MaxBytesToEmit is smaller than the padding200// size, the fixup encodes MaxBytesToEmit in the higher bits and references a201// per-section marker symbol.202bool LoongArchAsmBackend::relaxAlign(MCFragment &F, unsigned &Size) {203 // Alignments before the first linker-relaxable instruction have fixed sizes204 // and do not require relocations. Alignments after a linker-relaxable205 // instruction require a relocation, even if the STI specifies norelax.206 //207 // firstLinkerRelaxable is the layout order within the subsection, which may208 // be smaller than the section's order. Therefore, alignments in a209 // lower-numbered subsection may be unnecessarily treated as linker-relaxable.210 auto *Sec = F.getParent();211 if (F.getLayoutOrder() <= Sec->firstLinkerRelaxable())212 return false;213 214 // Use default handling unless linker relaxation is enabled and the215 // MaxBytesToEmit >= the nop size.216 const unsigned MinNopLen = 4;217 unsigned MaxBytesToEmit = F.getAlignMaxBytesToEmit();218 if (MaxBytesToEmit < MinNopLen)219 return false;220 221 Size = F.getAlignment().value() - MinNopLen;222 if (F.getAlignment() <= MinNopLen)223 return false;224 225 MCContext &Ctx = getContext();226 const MCExpr *Expr = nullptr;227 if (MaxBytesToEmit >= Size) {228 Expr = MCConstantExpr::create(Size, getContext());229 } else {230 MCSection *Sec = F.getParent();231 const MCSymbolRefExpr *SymRef = getSecToAlignSym()[Sec];232 if (SymRef == nullptr) {233 // Define a marker symbol at the section with an offset of 0.234 MCSymbol *Sym = Ctx.createNamedTempSymbol("la-relax-align");235 Sym->setFragment(&*Sec->getBeginSymbol()->getFragment());236 Asm->registerSymbol(*Sym);237 SymRef = MCSymbolRefExpr::create(Sym, Ctx);238 getSecToAlignSym()[Sec] = SymRef;239 }240 Expr = MCBinaryExpr::createAdd(241 SymRef,242 MCConstantExpr::create((MaxBytesToEmit << 8) | Log2(F.getAlignment()),243 Ctx),244 Ctx);245 }246 MCFixup Fixup =247 MCFixup::create(0, Expr, FirstLiteralRelocationKind + ELF::R_LARCH_ALIGN);248 F.setVarFixups({Fixup});249 F.setLinkerRelaxable();250 return true;251}252 253std::pair<bool, bool> LoongArchAsmBackend::relaxLEB128(MCFragment &F,254 int64_t &Value) const {255 const MCExpr &Expr = F.getLEBValue();256 if (F.isLEBSigned() || !Expr.evaluateKnownAbsolute(Value, *Asm))257 return std::make_pair(false, false);258 F.setVarFixups({MCFixup::create(0, &Expr, FK_Data_leb128)});259 return std::make_pair(true, true);260}261 262bool LoongArchAsmBackend::relaxDwarfLineAddr(MCFragment &F) const {263 MCContext &C = getContext();264 int64_t LineDelta = F.getDwarfLineDelta();265 const MCExpr &AddrDelta = F.getDwarfAddrDelta();266 int64_t Value;267 if (AddrDelta.evaluateAsAbsolute(Value, *Asm))268 return false;269 [[maybe_unused]] bool IsAbsolute =270 AddrDelta.evaluateKnownAbsolute(Value, *Asm);271 assert(IsAbsolute);272 273 SmallVector<char> Data;274 raw_svector_ostream OS(Data);275 276 // INT64_MAX is a signal that this is actually a DW_LNE_end_sequence.277 if (LineDelta != INT64_MAX) {278 OS << uint8_t(dwarf::DW_LNS_advance_line);279 encodeSLEB128(LineDelta, OS);280 }281 282 // According to the DWARF specification, the `DW_LNS_fixed_advance_pc` opcode283 // takes a single unsigned half (unencoded) operand. The maximum encodable284 // value is therefore 65535. Set a conservative upper bound for relaxation.285 unsigned PCBytes;286 if (Value > 60000) {287 unsigned PtrSize = C.getAsmInfo()->getCodePointerSize();288 assert((PtrSize == 4 || PtrSize == 8) && "Unexpected pointer size");289 PCBytes = PtrSize;290 OS << uint8_t(dwarf::DW_LNS_extended_op) << uint8_t(PtrSize + 1)291 << uint8_t(dwarf::DW_LNE_set_address);292 OS.write_zeros(PtrSize);293 } else {294 PCBytes = 2;295 OS << uint8_t(dwarf::DW_LNS_fixed_advance_pc);296 support::endian::write<uint16_t>(OS, 0, llvm::endianness::little);297 }298 auto Offset = OS.tell() - PCBytes;299 300 if (LineDelta == INT64_MAX) {301 OS << uint8_t(dwarf::DW_LNS_extended_op);302 OS << uint8_t(1);303 OS << uint8_t(dwarf::DW_LNE_end_sequence);304 } else {305 OS << uint8_t(dwarf::DW_LNS_copy);306 }307 308 F.setVarContents(Data);309 F.setVarFixups({MCFixup::create(Offset, &AddrDelta,310 MCFixup::getDataKindForSize(PCBytes))});311 return true;312}313 314bool LoongArchAsmBackend::relaxDwarfCFA(MCFragment &F) const {315 const MCExpr &AddrDelta = F.getDwarfAddrDelta();316 SmallVector<MCFixup, 2> Fixups;317 int64_t Value;318 if (AddrDelta.evaluateAsAbsolute(Value, *Asm))319 return false;320 bool IsAbsolute = AddrDelta.evaluateKnownAbsolute(Value, *Asm);321 assert(IsAbsolute && "CFA with invalid expression");322 (void)IsAbsolute;323 324 assert(getContext().getAsmInfo()->getMinInstAlignment() == 1 &&325 "expected 1-byte alignment");326 if (Value == 0) {327 F.clearVarContents();328 F.clearVarFixups();329 return true;330 }331 332 auto AddFixups = [&Fixups,333 &AddrDelta](unsigned Offset,334 std::pair<MCFixupKind, MCFixupKind> FK) {335 const MCBinaryExpr &MBE = cast<MCBinaryExpr>(AddrDelta);336 Fixups.push_back(MCFixup::create(Offset, MBE.getLHS(), std::get<0>(FK)));337 Fixups.push_back(MCFixup::create(Offset, MBE.getRHS(), std::get<1>(FK)));338 };339 340 SmallVector<char, 8> Data;341 raw_svector_ostream OS(Data);342 if (isUIntN(6, Value)) {343 OS << uint8_t(dwarf::DW_CFA_advance_loc);344 AddFixups(0, getRelocPairForSize(6));345 } else if (isUInt<8>(Value)) {346 OS << uint8_t(dwarf::DW_CFA_advance_loc1);347 support::endian::write<uint8_t>(OS, 0, llvm::endianness::little);348 AddFixups(1, getRelocPairForSize(8));349 } else if (isUInt<16>(Value)) {350 OS << uint8_t(dwarf::DW_CFA_advance_loc2);351 support::endian::write<uint16_t>(OS, 0, llvm::endianness::little);352 AddFixups(1, getRelocPairForSize(16));353 } else if (isUInt<32>(Value)) {354 OS << uint8_t(dwarf::DW_CFA_advance_loc4);355 support::endian::write<uint32_t>(OS, 0, llvm::endianness::little);356 AddFixups(1, getRelocPairForSize(32));357 } else {358 llvm_unreachable("unsupported CFA encoding");359 }360 F.setVarContents(Data);361 F.setVarFixups(Fixups);362 return true;363}364 365bool LoongArchAsmBackend::writeNopData(raw_ostream &OS, uint64_t Count,366 const MCSubtargetInfo *STI) const {367 // We mostly follow binutils' convention here: align to 4-byte boundary with a368 // 0-fill padding.369 OS.write_zeros(Count % 4);370 371 // The remainder is now padded with 4-byte nops.372 // nop: andi r0, r0, 0373 for (; Count >= 4; Count -= 4)374 OS.write("\0\0\x40\x03", 4);375 376 return true;377}378 379bool LoongArchAsmBackend::isPCRelFixupResolved(const MCSymbol *SymA,380 const MCFragment &F) {381 // If the section does not contain linker-relaxable fragments, PC-relative382 // fixups can be resolved.383 if (!F.getParent()->isLinkerRelaxable())384 return true;385 386 // Otherwise, check if the offset between the symbol and fragment is fully387 // resolved, unaffected by linker-relaxable fragments (e.g. instructions or388 // offset-affected FT_Align fragments). Complements the generic389 // isSymbolRefDifferenceFullyResolvedImpl.390 if (!PCRelTemp)391 PCRelTemp = getContext().createTempSymbol();392 PCRelTemp->setFragment(const_cast<MCFragment *>(&F));393 MCValue Res;394 MCExpr::evaluateSymbolicAdd(Asm, false, MCValue::get(SymA),395 MCValue::get(nullptr, PCRelTemp), Res);396 return !Res.getSubSym();397}398 399bool LoongArchAsmBackend::addReloc(const MCFragment &F, const MCFixup &Fixup,400 const MCValue &Target, uint64_t &FixedValue,401 bool IsResolved) {402 auto Fallback = [&]() {403 MCAsmBackend::maybeAddReloc(F, Fixup, Target, FixedValue, IsResolved);404 return true;405 };406 uint64_t FixedValueA, FixedValueB;407 if (Target.getSubSym()) {408 assert(Target.getSpecifier() == 0 &&409 "relocatable SymA-SymB cannot have relocation specifier");410 std::pair<MCFixupKind, MCFixupKind> FK;411 const MCSymbol &SA = *Target.getAddSym();412 const MCSymbol &SB = *Target.getSubSym();413 414 bool force = !SA.isInSection() || !SB.isInSection();415 if (!force) {416 const MCSection &SecA = SA.getSection();417 const MCSection &SecB = SB.getSection();418 const MCSection &SecCur = *F.getParent();419 420 // To handle the case of A - B which B is same section with the current,421 // generate PCRel relocations is better than ADD/SUB relocation pair.422 // We can resolve it as A - PC + PC - B. The A - PC will be resolved423 // as a PCRel relocation, while PC - B will serve as the addend.424 // If the linker relaxation is disabled, it can be done directly since425 // PC - B is constant. Otherwise, we should evaluate whether PC - B426 // is constant. If it can be resolved as PCRel, use Fallback which427 // generates R_LARCH_{32,64}_PCREL relocation later.428 if (&SecA != &SecB && &SecB == &SecCur &&429 isPCRelFixupResolved(Target.getSubSym(), F))430 return Fallback();431 432 // In SecA == SecB case. If the section is not linker-relaxable, the433 // FixedValue has already been calculated out in evaluateFixup,434 // return true and avoid record relocations.435 if (&SecA == &SecB && !SecA.isLinkerRelaxable())436 return true;437 }438 439 switch (Fixup.getKind()) {440 case llvm::FK_Data_1:441 FK = getRelocPairForSize(8);442 break;443 case llvm::FK_Data_2:444 FK = getRelocPairForSize(16);445 break;446 case llvm::FK_Data_4:447 FK = getRelocPairForSize(32);448 break;449 case llvm::FK_Data_8:450 FK = getRelocPairForSize(64);451 break;452 case llvm::FK_Data_leb128:453 FK = getRelocPairForSize(128);454 break;455 default:456 llvm_unreachable("unsupported fixup size");457 }458 MCValue A = MCValue::get(Target.getAddSym(), nullptr, Target.getConstant());459 MCValue B = MCValue::get(Target.getSubSym());460 auto FA = MCFixup::create(Fixup.getOffset(), nullptr, std::get<0>(FK));461 auto FB = MCFixup::create(Fixup.getOffset(), nullptr, std::get<1>(FK));462 Asm->getWriter().recordRelocation(F, FA, A, FixedValueA);463 Asm->getWriter().recordRelocation(F, FB, B, FixedValueB);464 FixedValue = FixedValueA - FixedValueB;465 return false;466 }467 468 // If linker relaxation is enabled and supported by the current relocation,469 // generate a relocation and then append a RELAX.470 if (Fixup.isLinkerRelaxable())471 IsResolved = false;472 if (IsResolved && Fixup.isPCRel())473 IsResolved = isPCRelFixupResolved(Target.getAddSym(), F);474 475 if (!IsResolved)476 Asm->getWriter().recordRelocation(F, Fixup, Target, FixedValue);477 478 if (Fixup.isLinkerRelaxable()) {479 auto FA = MCFixup::create(Fixup.getOffset(), nullptr, ELF::R_LARCH_RELAX);480 Asm->getWriter().recordRelocation(F, FA, MCValue::get(nullptr),481 FixedValueA);482 }483 484 return true;485}486 487std::unique_ptr<MCObjectTargetWriter>488LoongArchAsmBackend::createObjectTargetWriter() const {489 return createLoongArchELFObjectWriter(OSABI, Is64Bit);490}491 492MCAsmBackend *llvm::createLoongArchAsmBackend(const Target &T,493 const MCSubtargetInfo &STI,494 const MCRegisterInfo &MRI,495 const MCTargetOptions &Options) {496 const Triple &TT = STI.getTargetTriple();497 uint8_t OSABI = MCELFObjectTargetWriter::getOSABI(TT.getOS());498 return new LoongArchAsmBackend(STI, OSABI, TT.isArch64Bit(), Options);499}500