blob: af318b4d3ea282e0fec1a0c9f0f332ff4cc0c74e [file] [log] [blame]
//===-- RISCVMCExpr.cpp - RISC-V 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 RISC-V architecture (e.g. ":lo12:", ":gottprel_g1:", ...).
//
//===----------------------------------------------------------------------===//
#include "RISCVMCExpr.h"
#include "MCTargetDesc/RISCVAsmBackend.h"
#include "RISCVFixupKinds.h"
#include "llvm/BinaryFormat/ELF.h"
#include "llvm/MC/MCAssembler.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 "riscvmcexpr"
const RISCVMCExpr *RISCVMCExpr::create(const MCExpr *Expr, Specifier S,
MCContext &Ctx) {
return new (Ctx) RISCVMCExpr(Expr, S);
}
void RISCVMCExpr::printImpl(raw_ostream &OS, const MCAsmInfo *MAI) const {
Specifier S = getSpecifier();
bool HasVariant = ((S != VK_None) && (S != VK_CALL) && (S != VK_CALL_PLT));
if (HasVariant)
OS << '%' << getSpecifierName(S) << '(';
Expr->print(OS, MAI);
if (HasVariant)
OS << ')';
}
const MCFixup *RISCVMCExpr::getPCRelHiFixup(const MCFragment **DFOut) const {
MCValue AUIPCLoc;
if (!getSubExpr()->evaluateAsRelocatable(AUIPCLoc, nullptr))
return nullptr;
const MCSymbol *AUIPCSymbol = AUIPCLoc.getAddSym();
if (!AUIPCSymbol)
return nullptr;
const auto *DF = dyn_cast_or_null<MCDataFragment>(AUIPCSymbol->getFragment());
if (!DF)
return nullptr;
uint64_t Offset = AUIPCSymbol->getOffset();
if (DF->getContents().size() == Offset) {
DF = dyn_cast_or_null<MCDataFragment>(DF->getNext());
if (!DF)
return nullptr;
Offset = 0;
}
for (const MCFixup &F : DF->getFixups()) {
if (F.getOffset() != Offset)
continue;
auto Kind = F.getTargetKind();
if (!mc::isRelocation(F.getKind())) {
if (Kind == RISCV::fixup_riscv_pcrel_hi20) {
*DFOut = DF;
return &F;
}
break;
}
switch (Kind - FirstRelocationKind) {
case ELF::R_RISCV_GOT_HI20:
case ELF::R_RISCV_TLS_GOT_HI20:
case ELF::R_RISCV_TLS_GD_HI20:
case ELF::R_RISCV_TLSDESC_HI20:
*DFOut = DF;
return &F;
}
}
return nullptr;
}
bool RISCVMCExpr::evaluateAsRelocatableImpl(MCValue &Res,
const MCAssembler *Asm) const {
if (!getSubExpr()->evaluateAsRelocatable(Res, Asm))
return false;
Res.setSpecifier(specifier);
// Custom fixup types are not valid with symbol difference expressions.
return !Res.getSubSym();
}
void RISCVMCExpr::visitUsedExpr(MCStreamer &Streamer) const {
Streamer.visitUsedExpr(*getSubExpr());
}
std::optional<RISCVMCExpr::Specifier>
RISCVMCExpr::getSpecifierForName(StringRef name) {
return StringSwitch<std::optional<RISCVMCExpr::Specifier>>(name)
.Case("lo", VK_LO)
.Case("hi", VK_HI)
.Case("pcrel_lo", VK_PCREL_LO)
.Case("pcrel_hi", VK_PCREL_HI)
.Case("got_pcrel_hi", VK_GOT_HI)
.Case("tprel_lo", VK_TPREL_LO)
.Case("tprel_hi", VK_TPREL_HI)
.Case("tprel_add", VK_TPREL_ADD)
.Case("tls_ie_pcrel_hi", VK_TLS_GOT_HI)
.Case("tls_gd_pcrel_hi", VK_TLS_GD_HI)
.Case("tlsdesc_hi", VK_TLSDESC_HI)
.Case("tlsdesc_load_lo", VK_TLSDESC_LOAD_LO)
.Case("tlsdesc_add_lo", VK_TLSDESC_ADD_LO)
.Case("tlsdesc_call", VK_TLSDESC_CALL)
.Case("qc.abs20", VK_QC_ABS20)
// Used in data directives
.Case("pltpcrel", VK_PLTPCREL)
.Case("gotpcrel", VK_GOTPCREL)
.Default(std::nullopt);
}
StringRef RISCVMCExpr::getSpecifierName(Specifier S) {
switch (S) {
case VK_None:
llvm_unreachable("not used as %specifier()");
case VK_LO:
return "lo";
case VK_HI:
return "hi";
case VK_PCREL_LO:
return "pcrel_lo";
case VK_PCREL_HI:
return "pcrel_hi";
case VK_GOT_HI:
return "got_pcrel_hi";
case VK_TPREL_LO:
return "tprel_lo";
case VK_TPREL_HI:
return "tprel_hi";
case VK_TPREL_ADD:
return "tprel_add";
case VK_TLS_GOT_HI:
return "tls_ie_pcrel_hi";
case VK_TLSDESC_HI:
return "tlsdesc_hi";
case VK_TLSDESC_LOAD_LO:
return "tlsdesc_load_lo";
case VK_TLSDESC_ADD_LO:
return "tlsdesc_add_lo";
case VK_TLSDESC_CALL:
return "tlsdesc_call";
case VK_TLS_GD_HI:
return "tls_gd_pcrel_hi";
case VK_CALL:
return "call";
case VK_CALL_PLT:
return "call_plt";
case VK_32_PCREL:
return "32_pcrel";
case VK_GOTPCREL:
return "gotpcrel";
case VK_PLTPCREL:
return "pltpcrel";
case VK_QC_ABS20:
return "qc.abs20";
}
llvm_unreachable("Invalid ELF symbol kind");
}