| //===-- LoongArchMCExpr.cpp - LoongArch specific MC expression classes ----===// |
| // |
| // 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 |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This file contains the implementation of the assembly expression modifiers |
| // accepted by the LoongArch architecture. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "LoongArchMCExpr.h" |
| #include "LoongArchAsmBackend.h" |
| #include "LoongArchFixupKinds.h" |
| #include "llvm/MC/MCContext.h" |
| #include "llvm/MC/MCStreamer.h" |
| #include "llvm/MC/MCSymbolELF.h" |
| #include "llvm/MC/MCValue.h" |
| #include "llvm/Support/Casting.h" |
| #include "llvm/Support/ErrorHandling.h" |
| |
| using namespace llvm; |
| |
| #define DEBUG_TYPE "loongarch-mcexpr" |
| |
| const LoongArchMCExpr * |
| LoongArchMCExpr::create(const MCExpr *Expr, VariantKind Kind, MCContext &Ctx) { |
| return new (Ctx) LoongArchMCExpr(Expr, Kind); |
| } |
| |
| void LoongArchMCExpr::printImpl(raw_ostream &OS, const MCAsmInfo *MAI) const { |
| VariantKind Kind = getKind(); |
| bool HasVariant = |
| ((Kind != VK_LoongArch_None) && (Kind != VK_LoongArch_CALL)); |
| |
| if (HasVariant) |
| OS << '%' << getVariantKindName(getKind()) << '('; |
| Expr->print(OS, MAI); |
| if (HasVariant) |
| OS << ')'; |
| } |
| |
| bool LoongArchMCExpr::evaluateAsRelocatableImpl(MCValue &Res, |
| const MCAsmLayout *Layout, |
| const MCFixup *Fixup) const { |
| // Explicitly drop the layout and assembler to prevent any symbolic folding in |
| // the expression handling. This is required to preserve symbolic difference |
| // expressions to emit the paired relocations. |
| if (!getSubExpr()->evaluateAsRelocatable(Res, nullptr, nullptr)) |
| return false; |
| |
| Res = |
| MCValue::get(Res.getSymA(), Res.getSymB(), Res.getConstant(), getKind()); |
| // Custom fixup types are not valid with symbol difference expressions. |
| return Res.getSymB() ? getKind() == VK_LoongArch_None : true; |
| } |
| |
| void LoongArchMCExpr::visitUsedExpr(MCStreamer &Streamer) const { |
| Streamer.visitUsedExpr(*getSubExpr()); |
| } |
| |
| StringRef LoongArchMCExpr::getVariantKindName(VariantKind Kind) { |
| switch (Kind) { |
| default: |
| llvm_unreachable("Invalid ELF symbol kind"); |
| case VK_LoongArch_CALL_PLT: |
| return "plt"; |
| case VK_LoongArch_B16: |
| return "b16"; |
| case VK_LoongArch_B21: |
| return "b21"; |
| case VK_LoongArch_B26: |
| return "b21"; |
| case VK_LoongArch_ABS_HI20: |
| return "abs_hi20"; |
| case VK_LoongArch_ABS_LO12: |
| return "abs_lo12"; |
| case VK_LoongArch_ABS64_LO20: |
| return "abs64_lo20"; |
| case VK_LoongArch_ABS64_HI12: |
| return "abs64_hi12"; |
| case VK_LoongArch_PCALA_HI20: |
| return "pc_hi20"; |
| case VK_LoongArch_PCALA_LO12: |
| return "pc_lo12"; |
| case VK_LoongArch_PCALA64_LO20: |
| return "pc64_lo20"; |
| case VK_LoongArch_PCALA64_HI12: |
| return "pc64_hi12"; |
| case VK_LoongArch_GOT_PC_HI20: |
| return "got_pc_hi20"; |
| case VK_LoongArch_GOT_PC_LO12: |
| return "got_pc_lo12"; |
| case VK_LoongArch_GOT64_PC_LO20: |
| return "got64_pc_lo20"; |
| case VK_LoongArch_GOT64_PC_HI12: |
| return "got64_pc_hi12"; |
| case VK_LoongArch_GOT_HI20: |
| return "got_hi20"; |
| case VK_LoongArch_GOT_LO12: |
| return "got_lo12"; |
| case VK_LoongArch_GOT64_LO20: |
| return "got64_lo20"; |
| case VK_LoongArch_GOT64_HI12: |
| return "got64_hi12"; |
| case VK_LoongArch_TLS_LE_HI20: |
| return "le_hi20"; |
| case VK_LoongArch_TLS_LE_LO12: |
| return "le_lo12"; |
| case VK_LoongArch_TLS_LE64_LO20: |
| return "le64_lo20"; |
| case VK_LoongArch_TLS_LE64_HI12: |
| return "le64_hi12"; |
| case VK_LoongArch_TLS_IE_PC_HI20: |
| return "ie_pc_hi20"; |
| case VK_LoongArch_TLS_IE_PC_LO12: |
| return "ie_pc_lo12"; |
| case VK_LoongArch_TLS_IE64_PC_LO20: |
| return "ie64_pc_lo20"; |
| case VK_LoongArch_TLS_IE64_PC_HI12: |
| return "ie64_pc_hi12"; |
| case VK_LoongArch_TLS_IE_HI20: |
| return "ie_hi20"; |
| case VK_LoongArch_TLS_IE_LO12: |
| return "ie_lo12"; |
| case VK_LoongArch_TLS_IE64_LO20: |
| return "ie64_lo20"; |
| case VK_LoongArch_TLS_IE64_HI12: |
| return "ie64_hi12"; |
| case VK_LoongArch_TLS_LD_PC_HI20: |
| return "ld_pc_hi20"; |
| case VK_LoongArch_TLS_LD_HI20: |
| return "ld_hi20"; |
| case VK_LoongArch_TLS_GD_PC_HI20: |
| return "gd_pc_hi20"; |
| case VK_LoongArch_TLS_GD_HI20: |
| return "gd_hi20"; |
| } |
| } |
| |
| LoongArchMCExpr::VariantKind |
| LoongArchMCExpr::getVariantKindForName(StringRef name) { |
| return StringSwitch<LoongArchMCExpr::VariantKind>(name) |
| .Case("plt", VK_LoongArch_CALL_PLT) |
| .Case("b16", VK_LoongArch_B16) |
| .Case("b21", VK_LoongArch_B21) |
| .Case("b26", VK_LoongArch_B26) |
| .Case("abs_hi20", VK_LoongArch_ABS_HI20) |
| .Case("abs_lo12", VK_LoongArch_ABS_LO12) |
| .Case("abs64_lo20", VK_LoongArch_ABS64_LO20) |
| .Case("abs64_hi12", VK_LoongArch_ABS64_HI12) |
| .Case("pc_hi20", VK_LoongArch_PCALA_HI20) |
| .Case("pc_lo12", VK_LoongArch_PCALA_LO12) |
| .Case("pc64_lo20", VK_LoongArch_PCALA64_LO20) |
| .Case("pc64_hi12", VK_LoongArch_PCALA64_HI12) |
| .Case("got_pc_hi20", VK_LoongArch_GOT_PC_HI20) |
| .Case("got_pc_lo12", VK_LoongArch_GOT_PC_LO12) |
| .Case("got64_pc_lo20", VK_LoongArch_GOT64_PC_LO20) |
| .Case("got64_pc_hi12", VK_LoongArch_GOT64_PC_HI12) |
| .Case("got_hi20", VK_LoongArch_GOT_HI20) |
| .Case("got_lo12", VK_LoongArch_GOT_LO12) |
| .Case("got64_lo20", VK_LoongArch_GOT64_LO20) |
| .Case("got64_hi12", VK_LoongArch_GOT64_HI12) |
| .Case("le_hi20", VK_LoongArch_TLS_LE_HI20) |
| .Case("le_lo12", VK_LoongArch_TLS_LE_LO12) |
| .Case("le64_lo20", VK_LoongArch_TLS_LE64_LO20) |
| .Case("le64_hi12", VK_LoongArch_TLS_LE64_HI12) |
| .Case("ie_pc_hi20", VK_LoongArch_TLS_IE_PC_HI20) |
| .Case("ie_pc_lo12", VK_LoongArch_TLS_IE_PC_LO12) |
| .Case("ie64_pc_lo20", VK_LoongArch_TLS_IE64_PC_LO20) |
| .Case("ie64_pc_hi12", VK_LoongArch_TLS_IE64_PC_HI12) |
| .Case("ie_hi20", VK_LoongArch_TLS_IE_HI20) |
| .Case("ie_lo12", VK_LoongArch_TLS_IE_LO12) |
| .Case("ie64_lo20", VK_LoongArch_TLS_IE64_LO20) |
| .Case("ie64_hi12", VK_LoongArch_TLS_IE64_HI12) |
| .Case("ld_pc_hi20", VK_LoongArch_TLS_LD_PC_HI20) |
| .Case("ld_hi20", VK_LoongArch_TLS_LD_HI20) |
| .Case("gd_pc_hi20", VK_LoongArch_TLS_GD_PC_HI20) |
| .Case("gd_hi20", VK_LoongArch_TLS_GD_HI20) |
| .Default(VK_LoongArch_Invalid); |
| } |
| |
| static void fixELFSymbolsInTLSFixupsImpl(const MCExpr *Expr, MCAssembler &Asm) { |
| switch (Expr->getKind()) { |
| case MCExpr::Target: |
| llvm_unreachable("Can't handle nested target expression"); |
| break; |
| case MCExpr::Constant: |
| break; |
| case MCExpr::Unary: |
| fixELFSymbolsInTLSFixupsImpl(cast<MCUnaryExpr>(Expr)->getSubExpr(), Asm); |
| break; |
| case MCExpr::Binary: { |
| const MCBinaryExpr *BE = cast<MCBinaryExpr>(Expr); |
| fixELFSymbolsInTLSFixupsImpl(BE->getLHS(), Asm); |
| fixELFSymbolsInTLSFixupsImpl(BE->getRHS(), Asm); |
| break; |
| } |
| case MCExpr::SymbolRef: { |
| // We're known to be under a TLS fixup, so any symbol should be |
| // modified. There should be only one. |
| const MCSymbolRefExpr &SymRef = *cast<MCSymbolRefExpr>(Expr); |
| cast<MCSymbolELF>(SymRef.getSymbol()).setType(ELF::STT_TLS); |
| break; |
| } |
| } |
| } |
| |
| void LoongArchMCExpr::fixELFSymbolsInTLSFixups(MCAssembler &Asm) const { |
| switch (getKind()) { |
| default: |
| return; |
| case VK_LoongArch_TLS_LE_HI20: |
| case VK_LoongArch_TLS_IE_PC_HI20: |
| case VK_LoongArch_TLS_IE_HI20: |
| case VK_LoongArch_TLS_LD_PC_HI20: |
| case VK_LoongArch_TLS_LD_HI20: |
| case VK_LoongArch_TLS_GD_PC_HI20: |
| case VK_LoongArch_TLS_GD_HI20: |
| break; |
| } |
| fixELFSymbolsInTLSFixupsImpl(getSubExpr(), Asm); |
| } |