Index: include/llvm/MC/MCStreamer.h =================================================================== --- include/llvm/MC/MCStreamer.h +++ include/llvm/MC/MCStreamer.h @@ -28,6 +28,7 @@ class MCAsmBackend; class MCCodeEmitter; class MCContext; +class MCDataFragment; class MCExpr; class MCInst; class MCInstPrinter; @@ -501,6 +502,23 @@ /// etc. virtual void EmitBytes(StringRef Data); + /// Select an appropriate fixup for the given expression \p Value. + /// + /// This is used to implement assembler directives such as .word, .quad, + /// etc. Some targets can accept place-relative expressions. + /// + /// For example 'foo-.' on Mips can be supported with R_MIPS_PC32. + /// + /// \param Frag - The fragment the value will be written to. + /// \param Offset - The offset of the value within the fragment. + /// \param Value - The value to emit. + /// \param Size - The size of the integer (in bytes) to emit. This must + /// match a native machine width. + /// \param Loc - The location of the expression for error reporting. + virtual MCFixup GetFixupForValue(const MCFragment *Frag, uint64_t Offset, + const MCExpr *Value, unsigned Size, + const SMLoc &Loc = SMLoc()); + /// \brief Emit the expression \p Value into the output as a native /// integer of the given \p Size bytes. /// Index: lib/MC/MCObjectStreamer.cpp =================================================================== --- lib/MC/MCObjectStreamer.cpp +++ lib/MC/MCObjectStreamer.cpp @@ -143,8 +143,7 @@ return; } DF->getFixups().push_back( - MCFixup::create(DF->getContents().size(), Value, - MCFixup::getKindForSize(Size, false), Loc)); + GetFixupForValue(DF, DF->getContents().size(), Value, Size, Loc)); DF->getContents().resize(DF->getContents().size() + Size, 0); } Index: lib/MC/MCStreamer.cpp =================================================================== --- lib/MC/MCStreamer.cpp +++ lib/MC/MCStreamer.cpp @@ -106,6 +106,13 @@ EmitBytes(OSE.str()); } +MCFixup MCStreamer::GetFixupForValue(const MCFragment *, uint64_t Offset, + const MCExpr *Value, unsigned Size, + const SMLoc &Loc) { + return MCFixup::create(Offset, Value, MCFixup::getKindForSize(Size, false), + Loc); +} + void MCStreamer::EmitValue(const MCExpr *Value, unsigned Size, const SMLoc &Loc) { EmitValueImpl(Value, Size, Loc); Index: lib/Target/Mips/MCTargetDesc/MipsELFObjectWriter.cpp =================================================================== --- lib/Target/Mips/MCTargetDesc/MipsELFObjectWriter.cpp +++ lib/Target/Mips/MCTargetDesc/MipsELFObjectWriter.cpp @@ -79,6 +79,9 @@ case FK_Data_8: Type = ELF::R_MIPS_64; break; + case FK_PCRel_4: + Type = ELF::R_MIPS_PC32; + break; case FK_GPRel_4: if (isN64()) { Type = setRType((unsigned)ELF::R_MIPS_GPREL32, Type); Index: lib/Target/Mips/MCTargetDesc/MipsELFStreamer.h =================================================================== --- lib/Target/Mips/MCTargetDesc/MipsELFStreamer.h +++ lib/Target/Mips/MCTargetDesc/MipsELFStreamer.h @@ -58,6 +58,11 @@ void SwitchSection(MCSection *Section, const MCExpr *Subsection = nullptr) override; + /// Overriding to add support for place-relative values using R_MIPS_PC32. + MCFixup GetFixupForValue(const MCFragment *Frag, uint64_t Offset, + const MCExpr *Value, unsigned Size, + const SMLoc &Loc) override; + /// Overriding this function allows us to dismiss all labels that are /// candidates for marking as microMIPS when .word directive is emitted. void EmitValueImpl(const MCExpr *Value, unsigned Size, Index: lib/Target/Mips/MCTargetDesc/MipsELFStreamer.cpp =================================================================== --- lib/Target/Mips/MCTargetDesc/MipsELFStreamer.cpp +++ lib/Target/Mips/MCTargetDesc/MipsELFStreamer.cpp @@ -64,6 +64,30 @@ Labels.clear(); } +MCFixup MipsELFStreamer::GetFixupForValue(const MCFragment *Frag, + uint64_t Offset, const MCExpr *Value, + unsigned Size, const SMLoc &Loc) { + // Look for expressions of the form 'foo - bar' where bar is equivalent to the + // current position in the fragment. We can handle these with R_MIPS_PC32 when + // Size is 4. The 'PC' in this context is the position of the value. + if (const MCBinaryExpr *BinOp = dyn_cast(Value)) { + if (BinOp->getOpcode() == MCBinaryExpr::Sub) { + const MCSymbolRefExpr *LHS = dyn_cast(BinOp->getLHS()); + const MCSymbolRefExpr *RHS = dyn_cast(BinOp->getRHS()); + if (LHS && RHS) { + // Check the RHS is in the same fragment at the current position. + const MCSymbolData &SymData = RHS->getSymbol().getData(); + if (Frag == SymData.getFragment() && SymData.getOffset() == Offset && + Size == 4) + return MCFixup::create(Offset, LHS, + MCFixup::getKindForSize(Size, true), Loc); + } + } + } + + return MCELFStreamer::GetFixupForValue(Frag, Offset, Value, Size, Loc); +} + void MipsELFStreamer::EmitValueImpl(const MCExpr *Value, unsigned Size, const SMLoc &Loc) { MCELFStreamer::EmitValueImpl(Value, Size, Loc); Index: test/MC/Mips/data-relocations.s =================================================================== --- /dev/null +++ test/MC/Mips/data-relocations.s @@ -0,0 +1,5 @@ +# RUN: llvm-mc %s -triple=mipsel-unknown-linux -filetype=obj | llvm-readobj -r | FileCheck %s + .extern foo + .data + .word foo # CHECK: 0x0 R_MIPS_32 foo 0x0 + .word foo-. # CHECK: 0x4 R_MIPS_PC32 foo 0x0