/// instead of symbolic register names in .cfi_* directives.
bool DwarfRegNumForCFI; // Defaults to false;
+ /// UseParensForSymbolVariant - True if target uses parens to indicate the
+ /// symbol variant instead of @. For example, foo(plt) instead of foo@plt.
+ bool UseParensForSymbolVariant; // Defaults to false;
+
//===--- Prologue State ----------------------------------------------===//
std::vector<MCCFIInstruction> InitialFrameState;
bool useDwarfRegNumForCFI() const {
return DwarfRegNumForCFI;
}
+ bool useParensForSymbolVariant() const {
+ return UseParensForSymbolVariant;
+ }
void addInitialFrameState(const MCCFIInstruction &Inst) {
InitialFrameState.push_back(Inst);
#include "llvm/Support/DataTypes.h"
namespace llvm {
+class MCAsmInfo;
class MCAsmLayout;
class MCAssembler;
class MCContext;
VK_DTPOFF,
VK_TLVP, // Mach-O thread local variable relocation
VK_SECREL,
- // FIXME: We'd really like to use the generic Kinds listed above for these.
+
VK_ARM_NONE,
- VK_ARM_PLT, // ARM-style PLT references. i.e., (PLT) instead of @PLT
- VK_ARM_TLSGD, // ditto for TLSGD, GOT, GOTOFF, TPOFF and GOTTPOFF
- VK_ARM_GOT,
- VK_ARM_GOTOFF,
- VK_ARM_TPOFF,
- VK_ARM_GOTTPOFF,
VK_ARM_TARGET1,
VK_ARM_TARGET2,
VK_ARM_PREL31,
/// The symbol reference modifier.
const VariantKind Kind;
- explicit MCSymbolRefExpr(const MCSymbol *_Symbol, VariantKind _Kind)
- : MCExpr(MCExpr::SymbolRef), Symbol(_Symbol), Kind(_Kind) {
+ /// MCAsmInfo that is used to print symbol variants correctly.
+ const MCAsmInfo *MAI;
+
+ explicit MCSymbolRefExpr(const MCSymbol *_Symbol, VariantKind _Kind,
+ const MCAsmInfo *_MAI)
+ : MCExpr(MCExpr::SymbolRef), Symbol(_Symbol), Kind(_Kind), MAI(_MAI) {
assert(Symbol);
+ assert(MAI);
}
public:
/// @{
const MCSymbol &getSymbol() const { return *Symbol; }
+ const MCAsmInfo &getMCAsmInfo() const { return *MAI; }
VariantKind getKind() const { return Kind; }
DwarfRegNumForCFI = false;
HasMicrosoftFastStdCallMangling = false;
NeedsDwarfSectionOffsetDirective = false;
+ UseParensForSymbolVariant = false;
}
MCAsmInfo::~MCAsmInfo() {
case MCSymbolRefExpr::VK_TLSLDM:
case MCSymbolRefExpr::VK_TPOFF:
case MCSymbolRefExpr::VK_DTPOFF:
- case MCSymbolRefExpr::VK_ARM_TLSGD:
- case MCSymbolRefExpr::VK_ARM_TPOFF:
- case MCSymbolRefExpr::VK_ARM_GOTTPOFF:
case MCSymbolRefExpr::VK_Mips_TLSGD:
case MCSymbolRefExpr::VK_Mips_GOTTPREL:
case MCSymbolRefExpr::VK_Mips_TPREL_HI:
#include "llvm/MC/MCExpr.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/ADT/StringSwitch.h"
+#include "llvm/MC/MCAsmInfo.h"
#include "llvm/MC/MCAsmLayout.h"
#include "llvm/MC/MCAssembler.h"
#include "llvm/MC/MCContext.h"
else
OS << Sym;
- if (SRE.getKind() == MCSymbolRefExpr::VK_ARM_NONE ||
- SRE.getKind() == MCSymbolRefExpr::VK_ARM_PLT ||
- SRE.getKind() == MCSymbolRefExpr::VK_ARM_TLSGD ||
- SRE.getKind() == MCSymbolRefExpr::VK_ARM_GOT ||
- SRE.getKind() == MCSymbolRefExpr::VK_ARM_GOTOFF ||
- SRE.getKind() == MCSymbolRefExpr::VK_ARM_TPOFF ||
- SRE.getKind() == MCSymbolRefExpr::VK_ARM_GOTTPOFF ||
- SRE.getKind() == MCSymbolRefExpr::VK_ARM_TARGET1 ||
- SRE.getKind() == MCSymbolRefExpr::VK_ARM_TARGET2 ||
- SRE.getKind() == MCSymbolRefExpr::VK_ARM_PREL31)
- OS << MCSymbolRefExpr::getVariantKindName(SRE.getKind());
- else if (SRE.getKind() != MCSymbolRefExpr::VK_None)
- OS << '@' << MCSymbolRefExpr::getVariantKindName(SRE.getKind());
+ if (SRE.getKind() != MCSymbolRefExpr::VK_None) {
+ if (SRE.getMCAsmInfo().useParensForSymbolVariant())
+ OS << '(' << MCSymbolRefExpr::getVariantKindName(SRE.getKind()) << ')';
+ else
+ OS << '@' << MCSymbolRefExpr::getVariantKindName(SRE.getKind());
+ }
return;
}
const MCSymbolRefExpr *MCSymbolRefExpr::Create(const MCSymbol *Sym,
VariantKind Kind,
MCContext &Ctx) {
- return new (Ctx) MCSymbolRefExpr(Sym, Kind);
+ return new (Ctx) MCSymbolRefExpr(Sym, Kind, Ctx.getAsmInfo());
}
const MCSymbolRefExpr *MCSymbolRefExpr::Create(StringRef Name, VariantKind Kind,
case VK_DTPOFF: return "DTPOFF";
case VK_TLVP: return "TLVP";
case VK_SECREL: return "SECREL32";
- case VK_ARM_NONE: return "(NONE)";
- case VK_ARM_PLT: return "(PLT)";
- case VK_ARM_GOT: return "(GOT)";
- case VK_ARM_GOTOFF: return "(GOTOFF)";
- case VK_ARM_TPOFF: return "(tpoff)";
- case VK_ARM_GOTTPOFF: return "(gottpoff)";
- case VK_ARM_TLSGD: return "(tlsgd)";
- case VK_ARM_TARGET1: return "(target1)";
- case VK_ARM_TARGET2: return "(target2)";
- case VK_ARM_PREL31: return "(prel31)";
+ case VK_ARM_NONE: return "none";
+ case VK_ARM_TARGET1: return "target1";
+ case VK_ARM_TARGET2: return "target2";
+ case VK_ARM_PREL31: return "prel31";
case VK_PPC_LO: return "l";
case VK_PPC_HI: return "h";
case VK_PPC_HA: return "ha";
.Case("got@tlsld@h", VK_PPC_GOT_TLSLD_HI)
.Case("GOT@TLSLD@HA", VK_PPC_GOT_TLSLD_HA)
.Case("got@tlsld@ha", VK_PPC_GOT_TLSLD_HA)
+ .Case("NONE", VK_ARM_NONE)
+ .Case("none", VK_ARM_NONE)
+ .Case("TARGET1", VK_ARM_TARGET1)
+ .Case("target1", VK_ARM_TARGET1)
+ .Case("TARGET2", VK_ARM_TARGET2)
+ .Case("target2", VK_ARM_TARGET2)
+ .Case("PREL31", VK_ARM_PREL31)
+ .Case("prel31", VK_ARM_PREL31)
.Default(VK_Invalid);
}
return true;
}
}
+ // Parse symbol variant
+ std::pair<StringRef, StringRef> Split;
+ if (!MAI.useParensForSymbolVariant()) {
+ Split = Identifier.split('@');
+ } else if (Lexer.is(AsmToken::LParen)) {
+ Lexer.Lex(); // eat (
+ StringRef VName;
+ parseIdentifier(VName);
+ if (Lexer.isNot(AsmToken::RParen)) {
+ return Error(Lexer.getTok().getLoc(),
+ "unexpected token in variant, expected ')'");
+ }
+ Lexer.Lex(); // eat )
+ Split = std::make_pair(Identifier, VName);
+ }
EndLoc = SMLoc::getFromPointer(Identifier.end());
// This is a symbol reference.
StringRef SymbolName = Identifier;
MCSymbolRefExpr::VariantKind Variant = MCSymbolRefExpr::VK_None;
- std::pair<StringRef, StringRef> Split = Identifier.split('@');
// Lookup the symbol variant if used.
- if (Split.first.size() != Identifier.size()) {
+ if (Split.second.size()) {
Variant = MCSymbolRefExpr::getVariantKindForName(Split.second);
if (Variant != MCSymbolRefExpr::VK_Invalid) {
SymbolName = Split.first;
- } else if (MAI.doesAllowAtInName()) {
+ } else if (MAI.doesAllowAtInName() && !MAI.useParensForSymbolVariant()) {
Variant = MCSymbolRefExpr::VK_None;
} else {
Variant = MCSymbolRefExpr::VK_None;
getModifierVariantKind(ARMCP::ARMCPModifier Modifier) {
switch (Modifier) {
case ARMCP::no_modifier: return MCSymbolRefExpr::VK_None;
- case ARMCP::TLSGD: return MCSymbolRefExpr::VK_ARM_TLSGD;
- case ARMCP::TPOFF: return MCSymbolRefExpr::VK_ARM_TPOFF;
- case ARMCP::GOTTPOFF: return MCSymbolRefExpr::VK_ARM_GOTTPOFF;
- case ARMCP::GOT: return MCSymbolRefExpr::VK_ARM_GOT;
- case ARMCP::GOTOFF: return MCSymbolRefExpr::VK_ARM_GOTOFF;
+ case ARMCP::TLSGD: return MCSymbolRefExpr::VK_TLSGD;
+ case ARMCP::TPOFF: return MCSymbolRefExpr::VK_TPOFF;
+ case ARMCP::GOTTPOFF: return MCSymbolRefExpr::VK_GOTTPOFF;
+ case ARMCP::GOT: return MCSymbolRefExpr::VK_GOT;
+ case ARMCP::GOTOFF: return MCSymbolRefExpr::VK_GOTOFF;
}
llvm_unreachable("Invalid ARMCPModifier!");
}
}
case ARMII::MO_PLT:
- Expr = MCSymbolRefExpr::Create(Symbol, MCSymbolRefExpr::VK_ARM_PLT,
+ Expr = MCSymbolRefExpr::Create(Symbol, MCSymbolRefExpr::VK_PLT,
OutContext);
break;
}
case MCSymbolRefExpr::VK_None:
Type = ELF::R_ARM_REL32;
break;
- case MCSymbolRefExpr::VK_ARM_TLSGD:
+ case MCSymbolRefExpr::VK_TLSGD:
llvm_unreachable("unimplemented");
- case MCSymbolRefExpr::VK_ARM_GOTTPOFF:
+ case MCSymbolRefExpr::VK_GOTTPOFF:
Type = ELF::R_ARM_TLS_IE32;
break;
}
case ARM::fixup_arm_blx:
case ARM::fixup_arm_uncondbl:
switch (Modifier) {
- case MCSymbolRefExpr::VK_ARM_PLT:
+ case MCSymbolRefExpr::VK_PLT:
Type = ELF::R_ARM_PLT32;
break;
default:
case MCSymbolRefExpr::VK_ARM_NONE:
Type = ELF::R_ARM_NONE;
break;
- case MCSymbolRefExpr::VK_ARM_GOT:
+ case MCSymbolRefExpr::VK_GOT:
Type = ELF::R_ARM_GOT_BREL;
break;
- case MCSymbolRefExpr::VK_ARM_TLSGD:
+ case MCSymbolRefExpr::VK_TLSGD:
Type = ELF::R_ARM_TLS_GD32;
break;
- case MCSymbolRefExpr::VK_ARM_TPOFF:
+ case MCSymbolRefExpr::VK_TPOFF:
Type = ELF::R_ARM_TLS_LE32;
break;
- case MCSymbolRefExpr::VK_ARM_GOTTPOFF:
+ case MCSymbolRefExpr::VK_GOTTPOFF:
Type = ELF::R_ARM_TLS_IE32;
break;
case MCSymbolRefExpr::VK_None:
Type = ELF::R_ARM_ABS32;
break;
- case MCSymbolRefExpr::VK_ARM_GOTOFF:
+ case MCSymbolRefExpr::VK_GOTOFF:
Type = ELF::R_ARM_GOTOFF32;
break;
case MCSymbolRefExpr::VK_ARM_TARGET1:
// Exceptions handling
if (EnableARMEHABI)
ExceptionsType = ExceptionHandling::ARM;
+
+ // foo(plt) instead of foo@plt
+ UseParensForSymbolVariant = true;
}
; Non-PIC code can use initial-exec, PIC code has to use general dynamic.
; CHECK-NONPIC-LABEL: f1:
- ; CHECK-NONPIC: external_gd(gottpoff)
+ ; CHECK-NONPIC: external_gd(GOTTPOFF)
; CHECK-PIC-LABEL: f1:
- ; CHECK-PIC: external_gd(tlsgd)
+ ; CHECK-PIC: external_gd(TLSGD)
}
define i32* @f2() {
; Non-PIC code can use local exec, PIC code can use local dynamic,
; but that is not implemented, so falls back to general dynamic.
; CHECK-NONPIC-LABEL: f2:
- ; CHECK-NONPIC: internal_gd(tpoff)
+ ; CHECK-NONPIC: internal_gd(TPOFF)
; CHECK-PIC-LABEL: f2:
- ; CHECK-PIC: internal_gd(tlsgd)
+ ; CHECK-PIC: internal_gd(TLSGD)
}
; Non-PIC code can use initial exec, PIC should use local dynamic,
; but that is not implemented, so falls back to general dynamic.
; CHECK-NONPIC-LABEL: f3:
- ; CHECK-NONPIC: external_ld(gottpoff)
+ ; CHECK-NONPIC: external_ld(GOTTPOFF)
; CHECK-PIC-LABEL: f3:
- ; CHECK-PIC: external_ld(tlsgd)
+ ; CHECK-PIC: external_ld(TLSGD)
}
define i32* @f4() {
; Non-PIC code can use local exec, PIC code can use local dynamic,
; but that is not implemented, so it falls back to general dynamic.
; CHECK-NONPIC-LABEL: f4:
- ; CHECK-NONPIC: internal_ld(tpoff)
+ ; CHECK-NONPIC: internal_ld(TPOFF)
; CHECK-PIC-LABEL: f4:
- ; CHECK-PIC: internal_ld(tlsgd)
+ ; CHECK-PIC: internal_ld(TLSGD)
}
; Non-PIC and PIC code will use initial exec as specified.
; CHECK-NONPIC-LABEL: f5:
- ; CHECK-NONPIC: external_ie(gottpoff)
+ ; CHECK-NONPIC: external_ie(GOTTPOFF)
; CHECK-PIC-LABEL: f5:
- ; CHECK-PIC: external_ie(gottpoff)
+ ; CHECK-PIC: external_ie(GOTTPOFF)
}
define i32* @f6() {
; Non-PIC code can use local exec, PIC code use initial exec as specified.
; CHECK-NONPIC-LABEL: f6:
- ; CHECK-NONPIC: internal_ie(tpoff)
+ ; CHECK-NONPIC: internal_ie(TPOFF)
; CHECK-PIC-LABEL: f6:
- ; CHECK-PIC: internal_ie(gottpoff)
+ ; CHECK-PIC: internal_ie(GOTTPOFF)
}
; Non-PIC and PIC code will use local exec as specified.
; CHECK-NONPIC-LABEL: f7:
- ; CHECK-NONPIC: external_le(tpoff)
+ ; CHECK-NONPIC: external_le(TPOFF)
; CHECK-PIC-LABEL: f7:
- ; CHECK-PIC: external_le(tpoff)
+ ; CHECK-PIC: external_le(TPOFF)
}
define i32* @f8() {
; Non-PIC and PIC code will use local exec as specified.
; CHECK-NONPIC-LABEL: f8:
- ; CHECK-NONPIC: internal_le(tpoff)
+ ; CHECK-NONPIC: internal_le(TPOFF)
; CHECK-PIC-LABEL: f8:
- ; CHECK-PIC: internal_le(tpoff)
+ ; CHECK-PIC: internal_le(TPOFF)
}
; RUN: llc < %s -march=arm -mtriple=arm-linux-gnueabi | \
-; RUN: grep "i(tpoff)"
+; RUN: grep "i(TPOFF)"
; RUN: llc < %s -march=arm -mtriple=arm-linux-gnueabi | \
; RUN: grep "__aeabi_read_tp"
; RUN: llc < %s -march=arm -mtriple=arm-linux-gnueabi \
define i32 @f() {
; CHECK-NONPIC-LABEL: f:
; CHECK-NONPIC: ldr {{r.}}, [pc, {{r.}}]
-; CHECK-NONPIC: i(gottpoff)
+; CHECK-NONPIC: i(GOTTPOFF)
; CHECK-PIC-LABEL: f:
; CHECK-PIC: __tls_get_addr
entry:
define i32* @g() {
; CHECK-NONPIC-LABEL: g:
; CHECK-NONPIC: ldr {{r.}}, [pc, {{r.}}]
-; CHECK-NONPIC: i(gottpoff)
+; CHECK-NONPIC: i(GOTTPOFF)
; CHECK-PIC-LABEL: g:
; CHECK-PIC: __tls_get_addr
entry:
; RUN: llc < %s -mtriple=thumbv7-linux-gnueabi | \
-; RUN: grep "i(tpoff)"
+; RUN: grep "i(TPOFF)"
; RUN: llc < %s -mtriple=thumbv7-linux-gnueabi | \
; RUN: grep "__aeabi_read_tp"
; RUN: llc < %s -mtriple=thumbv7-linux-gnueabi \
; CHECK-NOT-PIC-LABEL: f:
; CHECK-NOT-PIC: add r0, pc
; CHECK-NOT-PIC: ldr r1, [r0]
-; CHECK-NOT-PIC: i(gottpoff)
+; CHECK-NOT-PIC: i(GOTTPOFF)
; CHECK-PIC-LABEL: f:
; CHECK-PIC: bl __tls_get_addr(PLT)
; CHECK-NOT-PIC-LABEL: g:
; CHECK-NOT-PIC: add r0, pc
; CHECK-NOT-PIC: ldr r1, [r0]
-; CHECK-NOT-PIC: i(gottpoff)
+; CHECK-NOT-PIC: i(GOTTPOFF)
; CHECK-PIC-LABEL: g:
; CHECK-PIC: bl __tls_get_addr(PLT)
--- /dev/null
+@ RUN: not llvm-mc < %s -triple armv7-none-linux-gnueabi 2>&1 | FileCheck %s
+
+@ check for invalid variant
+f1:
+ bl bar(blargh)
+@CHECK: error: invalid variant 'blargh'
+@CHECK: bl bar(blargh)
+@CHECK: ^
+
+@ check for missing closed paren
+f2:
+ .word bar(got
+@CHECK: error: unexpected token in variant, expected ')'
+@CHECK: .word bar(got
+@CHECK: ^
+
+@ check for invalid symbol before variant end
+f3:
+ .word bar(got+2)
+
+@CHECK: error: unexpected token in variant, expected ')'
+@CHECK: .word bar(got+2)
+@CHECK: ^
--- /dev/null
+@ RUN: llvm-mc < %s -triple armv7-none-linux-gnueabi -filetype=obj | llvm-objdump -triple armv7-none-linux-gnueabi -r - | FileCheck %s --check-prefix=CHECK --check-prefix=ARM
+@ RUN: llvm-mc < %s -triple thumbv7-none-linux-gnueabi -filetype=obj | llvm-objdump -triple thumbv7-none-linux-gnueabi -r - | FileCheck %s --check-prefix=CHECK --check-prefix=THUMB
+
+@ CHECK-LABEL: RELOCATION RECORDS FOR [.rel.text]
+
+@ empty
+.word f00
+.word f01
+@CHECK: 0 R_ARM_ABS32 f00
+@CHECK: 4 R_ARM_ABS32 f01
+
+@ none
+.word f02(NONE)
+.word f03(none)
+@CHECK: 8 R_ARM_NONE f02
+@CHECK: 12 R_ARM_NONE f03
+
+@ plt
+bl f04(PLT)
+bl f05(plt)
+@ARM: 16 R_ARM_PLT32 f04
+@ARM: 20 R_ARM_PLT32 f05
+@THUMB: 16 R_ARM_THM_CALL f04
+@THUMB: 20 R_ARM_THM_CALL f05
+
+@ got
+.word f06(GOT)
+.word f07(got)
+@CHECK: 24 R_ARM_GOT_BREL f06
+@CHECK: 28 R_ARM_GOT_BREL f07
+
+@ gotoff
+.word f08(GOTOFF)
+.word f09(gotoff)
+@CHECK: 32 R_ARM_GOTOFF32 f08
+@CHECK: 36 R_ARM_GOTOFF32 f09
+
+@ tpoff
+.word f10(TPOFF)
+.word f11(tpoff)
+@CHECK: 40 R_ARM_TLS_LE32 f10
+@CHECK: 44 R_ARM_TLS_LE32 f11
+
+@ tlsgd
+.word f12(TLSGD)
+.word f13(tlsgd)
+@CHECK: 48 R_ARM_TLS_GD32 f12
+@CHECK: 52 R_ARM_TLS_GD32 f13
+
+@ target1
+.word f14(TARGET1)
+.word f15(target1)
+@CHECK: 56 R_ARM_TARGET1 f14
+@CHECK: 60 R_ARM_TARGET1 f15
+
+@ target2
+.word f16(TARGET2)
+.word f17(target2)
+@CHECK: 64 R_ARM_TARGET2 f16
+@CHECK: 68 R_ARM_TARGET2 f17
+
+@ prel31
+.word f18(PREL31)
+.word f19(prel31)
+@CHECK: 72 R_ARM_PREL31 f18
+@CHECK: 76 R_ARM_PREL31 f19
+