For RISC-V it is desirable to have relaxation happen in the linker once addresses are known, and as such the size between two instructions/byte sequences in a section could change. For most assembler expressions, this is fine, as the absolute address results in the expression being converted to a fixup, and finally relocations. However, for expressions such as .quad .L2-.L1, the assembler folds this down to a constant once fragments are laid out, under the assumption that the difference can no longer change, although in the case of linker relaxation the differences can change at link time, so the constant is incorrect. One place where this commonly appears is in debug information, where the size of a function expression is in a form similar to the above. This patch extends the assembler to allow an AsmBackend to declare that it does not want the assembler to fold down this expression, and instead generate a pair of relocations that allow the linker to carry out the calculation. In this case, the expression is not folded, but when it comes to emitting a fixup, the generic FK_Data_* fixups are converted into a pair, one for the addition half, one for the subtraction, and this is passed to the relocation generating methods as usual. I have named these FK_Data_Add_* and FK_Data_Sub_* to indicate which half these are for. For RISC-V, which supports this via e.g. the R_RISCV_ADD64, R_RISCV_SUB64 pair of relocations, these are also set to always emit relocations relative to local symbols rather than section offsets. This is to deal with the fact that if relocations were calculated on e.g. .text+8 and .text+4, the result 12 would be stored rather than 4 as both addends are added in the linker. Differential Revision: https://reviews.llvm.org/D45181 Patch by Simon Cook. llvm-svn: 333079
122 lines
3.4 KiB
C++
122 lines
3.4 KiB
C++
//===-- RISCVMCExpr.cpp - RISCV specific MC expression classes ------------===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file contains the implementation of the assembly expression modifiers
|
|
// accepted by the RISCV architecture (e.g. ":lo12:", ":gottprel_g1:", ...).
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "RISCV.h"
|
|
#include "RISCVMCExpr.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/Object/ELF.h"
|
|
#include "llvm/Support/ErrorHandling.h"
|
|
|
|
using namespace llvm;
|
|
|
|
#define DEBUG_TYPE "riscvmcexpr"
|
|
|
|
const RISCVMCExpr *RISCVMCExpr::create(const MCExpr *Expr, VariantKind Kind,
|
|
MCContext &Ctx) {
|
|
return new (Ctx) RISCVMCExpr(Expr, Kind);
|
|
}
|
|
|
|
void RISCVMCExpr::printImpl(raw_ostream &OS, const MCAsmInfo *MAI) const {
|
|
bool HasVariant =
|
|
((getKind() != VK_RISCV_None) && (getKind() != VK_RISCV_CALL));
|
|
if (HasVariant)
|
|
OS << '%' << getVariantKindName(getKind()) << '(';
|
|
Expr->print(OS, MAI);
|
|
if (HasVariant)
|
|
OS << ')';
|
|
}
|
|
|
|
bool RISCVMCExpr::evaluateAsRelocatableImpl(MCValue &Res,
|
|
const MCAsmLayout *Layout,
|
|
const MCFixup *Fixup) const {
|
|
if (!getSubExpr()->evaluateAsRelocatable(Res, Layout, Fixup))
|
|
return false;
|
|
|
|
// Some custom fixup types are not valid with symbol difference expressions
|
|
if (Res.getSymA() && Res.getSymB()) {
|
|
switch (getKind()) {
|
|
default:
|
|
return true;
|
|
case VK_RISCV_LO:
|
|
case VK_RISCV_HI:
|
|
case VK_RISCV_PCREL_LO:
|
|
case VK_RISCV_PCREL_HI:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void RISCVMCExpr::visitUsedExpr(MCStreamer &Streamer) const {
|
|
Streamer.visitUsedExpr(*getSubExpr());
|
|
}
|
|
|
|
RISCVMCExpr::VariantKind RISCVMCExpr::getVariantKindForName(StringRef name) {
|
|
return StringSwitch<RISCVMCExpr::VariantKind>(name)
|
|
.Case("lo", VK_RISCV_LO)
|
|
.Case("hi", VK_RISCV_HI)
|
|
.Case("pcrel_lo", VK_RISCV_PCREL_LO)
|
|
.Case("pcrel_hi", VK_RISCV_PCREL_HI)
|
|
.Default(VK_RISCV_Invalid);
|
|
}
|
|
|
|
StringRef RISCVMCExpr::getVariantKindName(VariantKind Kind) {
|
|
switch (Kind) {
|
|
default:
|
|
llvm_unreachable("Invalid ELF symbol kind");
|
|
case VK_RISCV_LO:
|
|
return "lo";
|
|
case VK_RISCV_HI:
|
|
return "hi";
|
|
case VK_RISCV_PCREL_LO:
|
|
return "pcrel_lo";
|
|
case VK_RISCV_PCREL_HI:
|
|
return "pcrel_hi";
|
|
}
|
|
}
|
|
|
|
bool RISCVMCExpr::evaluateAsConstant(int64_t &Res) const {
|
|
MCValue Value;
|
|
|
|
if (Kind == VK_RISCV_PCREL_HI || Kind == VK_RISCV_PCREL_LO ||
|
|
Kind == VK_RISCV_CALL)
|
|
return false;
|
|
|
|
if (!getSubExpr()->evaluateAsRelocatable(Value, nullptr, nullptr))
|
|
return false;
|
|
|
|
if (!Value.isAbsolute())
|
|
return false;
|
|
|
|
Res = evaluateAsInt64(Value.getConstant());
|
|
return true;
|
|
}
|
|
|
|
int64_t RISCVMCExpr::evaluateAsInt64(int64_t Value) const {
|
|
switch (Kind) {
|
|
default:
|
|
llvm_unreachable("Invalid kind");
|
|
case VK_RISCV_LO:
|
|
return SignExtend64<12>(Value);
|
|
case VK_RISCV_HI:
|
|
// Add 1 if bit 11 is 1, to compensate for low 12 bits being negative.
|
|
return ((Value + 0x800) >> 12) & 0xfffff;
|
|
}
|
|
}
|