diff --git a/llvm/include/llvm/MC/MCStreamer.h b/llvm/include/llvm/MC/MCStreamer.h --- a/llvm/include/llvm/MC/MCStreamer.h +++ b/llvm/include/llvm/MC/MCStreamer.h @@ -19,6 +19,7 @@ #include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSet.h" #include "llvm/MC/MCDirectives.h" #include "llvm/MC/MCLinkerOptimizationHint.h" #include "llvm/MC/MCPseudoProbe.h" @@ -237,6 +238,19 @@ /// discussion for future inclusion. bool AllowAutoPadding = false; + /// Tracks the symbols we have emitted for conditional assignments. + StringSet<> emittedSymbols; + + struct PendingAssignment { + MCSymbol *Symbol; + const MCExpr *Value; + MCSymbolAttr Attr; + }; + + /// A list of conditional assignments we may need to emit if the target + /// symbol is later emitted. + StringMap> pendingAssignments; + protected: MCStreamer(MCContext &Ctx); @@ -256,6 +270,10 @@ /// Returns true if the the .cv_loc directive is in the right section. bool checkCVLocSection(unsigned FuncId, unsigned FileNo, SMLoc Loc); + /// Emits pending conditional assignments that depend on \p Symbol + /// being emitted. + void emitPendingAssignments(MCSymbol *Symbol); + public: MCStreamer(const MCStreamer &) = delete; MCStreamer &operator=(const MCStreamer &) = delete; @@ -516,6 +534,11 @@ /// \param Value - The value for the symbol. virtual void emitAssignment(MCSymbol *Symbol, const MCExpr *Value); + /// Emit an assignment of \p Value to \p Symbol, but only if \p Value is also + /// emitted. If \p Attr is specified, also emit the symbol attribute. + virtual void emitConditionalAssignment(MCSymbol *Symbol, const MCExpr *Value, + MCSymbolAttr Attr = MCSA_Invalid); + /// Emit an weak reference from \p Alias to \p Symbol. /// /// This corresponds to an assembler statement such as: diff --git a/llvm/lib/MC/MCParser/AsmParser.cpp b/llvm/lib/MC/MCParser/AsmParser.cpp --- a/llvm/lib/MC/MCParser/AsmParser.cpp +++ b/llvm/lib/MC/MCParser/AsmParser.cpp @@ -357,7 +357,7 @@ StringRef parseStringToComma(); bool parseAssignment(StringRef Name, bool allow_redef, - bool NoDeadStrip = false); + bool NoDeadStrip = false, bool Cond = false); unsigned getBinOpPrecedence(AsmToken::TokenKind K, MCBinaryExpr::Opcode &Kind); @@ -534,6 +534,7 @@ DK_ADDRSIG_SYM, DK_PSEUDO_PROBE, DK_LTO_DISCARD, + DK_SET_CONDITIONAL, DK_END }; @@ -564,8 +565,8 @@ const fltSemantics &); // ".single", ... bool parseDirectiveFill(); // ".fill" bool parseDirectiveZero(); // ".zero" - // ".set", ".equ", ".equiv" - bool parseDirectiveSet(StringRef IDVal, bool allow_redef); + // ".set", ".equ", ".equiv", ".set_conditional" + bool parseDirectiveSet(StringRef IDVal, bool allow_redef, bool Cond = false); bool parseDirectiveOrg(); // ".org" // ".align{,32}", ".p2align{,w,l}" bool parseDirectiveAlign(bool IsPow2, unsigned ValueSize); @@ -2030,6 +2031,8 @@ return parseDirectiveSet(IDVal, true); case DK_EQUIV: return parseDirectiveSet(IDVal, false); + case DK_SET_CONDITIONAL: + return parseDirectiveSet(IDVal, true, true); case DK_ASCII: return parseDirectiveAscii(IDVal, false); case DK_ASCIZ: @@ -2926,9 +2929,10 @@ } bool AsmParser::parseAssignment(StringRef Name, bool allow_redef, - bool NoDeadStrip) { + bool NoDeadStrip, bool Cond) { MCSymbol *Sym; const MCExpr *Value; + SMLoc ExprLoc = getTok().getLoc(); if (MCParserUtils::parseAssignmentExpression(Name, allow_redef, *this, Sym, Value)) return true; @@ -2944,9 +2948,17 @@ return false; // Do the assignment. - Out.emitAssignment(Sym, Value); - if (NoDeadStrip) - Out.emitSymbolAttribute(Sym, MCSA_NoDeadStrip); + if (Cond) { + if (Value->getKind() != MCExpr::SymbolRef) + return Error(ExprLoc, "expected identifier"); + + Out.emitConditionalAssignment( + Sym, Value, NoDeadStrip ? MCSA_NoDeadStrip : MCSA_Invalid); + } else { + Out.emitAssignment(Sym, Value); + if (NoDeadStrip) + Out.emitSymbolAttribute(Sym, MCSA_NoDeadStrip); + } return false; } @@ -2998,10 +3010,12 @@ /// ::= .equ identifier ',' expression /// ::= .equiv identifier ',' expression /// ::= .set identifier ',' expression -bool AsmParser::parseDirectiveSet(StringRef IDVal, bool allow_redef) { +/// ::= .set_conditional identifier ',' expression +bool AsmParser::parseDirectiveSet(StringRef IDVal, bool allow_redef, + bool Cond) { StringRef Name; if (check(parseIdentifier(Name), "expected identifier") || parseComma() || - parseAssignment(Name, allow_redef, true)) + parseAssignment(Name, allow_redef, true, Cond)) return true; return false; } @@ -5581,6 +5595,7 @@ DirectiveKindMap[".addrsig_sym"] = DK_ADDRSIG_SYM; DirectiveKindMap[".pseudoprobe"] = DK_PSEUDO_PROBE; DirectiveKindMap[".lto_discard"] = DK_LTO_DISCARD; + DirectiveKindMap[".set_conditional"] = DK_SET_CONDITIONAL; } MCAsmMacro *AsmParser::parseMacroLikeBody(SMLoc DirectiveLoc) { diff --git a/llvm/lib/MC/MCStreamer.cpp b/llvm/lib/MC/MCStreamer.cpp --- a/llvm/lib/MC/MCStreamer.cpp +++ b/llvm/lib/MC/MCStreamer.cpp @@ -429,6 +429,38 @@ MCTargetStreamer *TS = getTargetStreamer(); if (TS) TS->emitLabel(Symbol); + + emitPendingAssignments(Symbol); +} + +void MCStreamer::emitPendingAssignments(MCSymbol *Symbol) { + auto Assignments = pendingAssignments.find(Symbol->getName()); + if (Assignments != pendingAssignments.end()) { + for (const auto &A : Assignments->second) { + emitAssignment(A.Symbol, A.Value); + if (A.Attr != MCSA_Invalid) + emitSymbolAttribute(A.Symbol, A.Attr); + } + + pendingAssignments.erase(Assignments); + } + + emittedSymbols.insert(Symbol->getName()); +} + +void MCStreamer::emitConditionalAssignment(MCSymbol *Symbol, + const MCExpr *Value, + MCSymbolAttr Attr) { + StringRef Target = cast(*Value).getSymbol().getName(); + + // If the symbol already exists, emit the assignment. Otherwise, emit it + // later only if the symbol is also emitted. + if (emittedSymbols.contains(Target)) { + emitAssignment(Symbol, Value); + if (Attr != MCSA_Invalid) + emitSymbolAttribute(Symbol, Attr); + } else + pendingAssignments[Target].push_back({Symbol, Value, Attr}); } void MCStreamer::emitCFISections(bool EH, bool Debug) {} @@ -1046,6 +1078,8 @@ MCTargetStreamer *TS = getTargetStreamer(); if (TS) TS->emitAssignment(Symbol, Value); + + emitPendingAssignments(Symbol); } void MCTargetStreamer::prettyPrintAsm(MCInstPrinter &InstPrinter, diff --git a/llvm/lib/Transforms/IPO/ThinLTOBitcodeWriter.cpp b/llvm/lib/Transforms/IPO/ThinLTOBitcodeWriter.cpp --- a/llvm/lib/Transforms/IPO/ThinLTOBitcodeWriter.cpp +++ b/llvm/lib/Transforms/IPO/ThinLTOBitcodeWriter.cpp @@ -87,7 +87,7 @@ if (isa(&ExportGV) && allowPromotionAlias(OldName)) { // Create a local alias with the original name to avoid breaking // references from inline assembly. - std::string Alias = ".set " + OldName + "," + NewName + "\n"; + std::string Alias = ".set_conditional " + OldName + "," + NewName + "\n"; ExportM.appendModuleInlineAsm(Alias); } } diff --git a/llvm/test/MC/ELF/set-conditional.s b/llvm/test/MC/ELF/set-conditional.s new file mode 100644 --- /dev/null +++ b/llvm/test/MC/ELF/set-conditional.s @@ -0,0 +1,31 @@ +# RUN: llvm-mc -triple x86_64-pc-linux-gnu < %s | FileCheck %s +# RUN: not llvm-mc -filetype=obj -triple x86_64-pc-linux-gnu --defsym ERR=1 %s 2>&1 |\ +# RUN: FileCheck %s --check-prefix=ERR + +# CHECK: .set b, a +# CHECK: .set c, b +# CHECK: .set d, a +.set_conditional b, a +.set_conditional d, a +.set_conditional c, b + +# CHECK-NOT: .set e, n +.set_conditional e, n + +# CHECK: a: +# CHECK-NEXT: .byte 1 +a: + .byte 1 + +# CHECK: .set f, a +.set_conditional f, a +# CHECK: .set g, b +.set_conditional g, b +# CHECK-NOT: .set h, m +.set_conditional h, m + +.ifdef ERR +.text +# ERR: {{.*}}.s:[[#@LINE+1]]:21: error: expected identifier +.set_conditional i, ERR +.endif diff --git a/llvm/test/MC/MachO/set-conditional.s b/llvm/test/MC/MachO/set-conditional.s new file mode 100644 --- /dev/null +++ b/llvm/test/MC/MachO/set-conditional.s @@ -0,0 +1,37 @@ +# RUN: llvm-mc -triple i386-apple-darwin9 < %s | FileCheck %s +# RUN: not llvm-mc -filetype=obj -triple i386-apple-darwin9 --defsym ERR=1 %s 2>&1 |\ +# RUN: FileCheck %s --check-prefix=ERR + +# CHECK: .set b, a +# CHECK: .set c, b +# CHECK: .no_dead_strip c +# CHECK: .no_dead_strip b +# CHECK: .set d, a +# CHECK: .no_dead_strip d +.set_conditional b, a +.set_conditional d, a +.set_conditional c, b +# CHECK-NOT: .set e, n +# CHECK-NOT: .no_dead_strip e +.set_conditional e, n + +# CHECK: a: +# CHECK-NEXT: .byte 1 +a: + .byte 1 + +# CHECK: .set f, a +# CHECK: .no_dead_strip f +.set_conditional f, a +# CHECK: .set g, b +# CHECK: .no_dead_strip g +.set_conditional g, b +# CHECK-NOT: .set h, m +# CHECK-NOT: .no_dead_strip h +.set_conditional h, m + +.ifdef ERR +.text +# ERR: {{.*}}.s:[[#@LINE+1]]:21: error: expected identifier +.set_conditional i, ERR +.endif diff --git a/llvm/test/ThinLTO/X86/devirt2.ll b/llvm/test/ThinLTO/X86/devirt2.ll --- a/llvm/test/ThinLTO/X86/devirt2.ll +++ b/llvm/test/ThinLTO/X86/devirt2.ll @@ -131,12 +131,10 @@ ; RUN: -r=%t1.o,_ZN1D1mEi, \ ; RUN: -r=%t1.o,test2, \ ; RUN: -r=%t2.o,_ZN1A1nEi,p \ -; RUN: -r=%t2.o,_ZN1A1nEi, \ ; RUN: -r=%t2.o,_ZN1B1fEi,p \ ; RUN: -r=%t2.o,_ZN1C1fEi,p \ ; RUN: -r=%t2.o,_ZN1D1mEi,p \ ; RUN: -r=%t2.o,_ZN1E1mEi,p \ -; RUN: -r=%t2.o,_ZN1E1mEi, \ ; RUN: -r=%t2.o,_ZTV1B, \ ; RUN: -r=%t2.o,_ZTV1C, \ ; RUN: -r=%t2.o,_ZTV1D, \ @@ -169,12 +167,10 @@ ; RUN: -r=%t1.o,_ZN1D1mEi, \ ; RUN: -r=%t1.o,test2, \ ; RUN: -r=%t2.o,_ZN1A1nEi,p \ -; RUN: -r=%t2.o,_ZN1A1nEi, \ ; RUN: -r=%t2.o,_ZN1B1fEi,p \ ; RUN: -r=%t2.o,_ZN1C1fEi,p \ ; RUN: -r=%t2.o,_ZN1D1mEi,p \ ; RUN: -r=%t2.o,_ZN1E1mEi,p \ -; RUN: -r=%t2.o,_ZN1E1mEi, \ ; RUN: -r=%t2.o,_ZTV1B, \ ; RUN: -r=%t2.o,_ZTV1C, \ ; RUN: -r=%t2.o,_ZTV1D, \ diff --git a/llvm/test/Transforms/ThinLTOBitcodeWriter/cfi-icall-static-inline-asm.ll b/llvm/test/Transforms/ThinLTOBitcodeWriter/cfi-icall-static-inline-asm.ll --- a/llvm/test/Transforms/ThinLTOBitcodeWriter/cfi-icall-static-inline-asm.ll +++ b/llvm/test/Transforms/ThinLTOBitcodeWriter/cfi-icall-static-inline-asm.ll @@ -3,7 +3,7 @@ target triple = "x86_64-unknown-linux-gnu" -; CHECK: module asm ".set a,a.[[HASH:[0-9a-f]+]]" +; CHECK: module asm ".set_conditional a,a.[[HASH:[0-9a-f]+]]" define void @b() { %f = alloca void ()*, align 8