diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -7153,7 +7153,8 @@ auto FRecordSwitches = Args.hasFlag(options::OPT_frecord_command_line, options::OPT_fno_record_command_line, false); - if (FRecordSwitches && !Triple.isOSBinFormatELF()) + if (FRecordSwitches && !Triple.isOSBinFormatELF() && + !Triple.isOSBinFormatXCOFF()) D.Diag(diag::err_drv_unsupported_opt_for_target) << Args.getLastArg(options::OPT_frecord_command_line)->getAsString(Args) << TripleStr; diff --git a/llvm/include/llvm/CodeGen/AsmPrinter.h b/llvm/include/llvm/CodeGen/AsmPrinter.h --- a/llvm/include/llvm/CodeGen/AsmPrinter.h +++ b/llvm/include/llvm/CodeGen/AsmPrinter.h @@ -866,7 +866,7 @@ /// Emit llvm.ident metadata in an '.ident' directive. void emitModuleIdents(Module &M); /// Emit bytes for llvm.commandline metadata. - void emitModuleCommandLines(Module &M); + virtual void emitModuleCommandLines(Module &M); GCMetadataPrinter *getOrCreateGCPrinter(GCStrategy &S); void emitGlobalAlias(Module &M, const GlobalAlias &GA); 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 @@ -647,6 +647,12 @@ /// \param Sym - The symbol on the .ref directive. virtual void emitXCOFFRefDirective(const MCSymbol *Symbol); + /// Emit a C_INFO symbol with XCOFF embedded metadata to the .info section. + /// + /// \param Name - The embedded metadata name + /// \param Metadata - The embedded metadata + virtual void emitXCOFFCInfoSym(StringRef Name, StringRef Metadata); + /// Emit an ELF .size directive. /// /// This corresponds to an assembler statement such as: diff --git a/llvm/include/llvm/MC/MCXCOFFStreamer.h b/llvm/include/llvm/MC/MCXCOFFStreamer.h --- a/llvm/include/llvm/MC/MCXCOFFStreamer.h +++ b/llvm/include/llvm/MC/MCXCOFFStreamer.h @@ -40,6 +40,10 @@ void emitXCOFFExceptDirective(const MCSymbol *Symbol, const MCSymbol *Trap, unsigned Lang, unsigned Reason, unsigned FunctionSize, bool hasDebug) override; + void emitXCOFFCInfoSym(StringRef Name, StringRef Metadata) override { + report_fatal_error("emitXCOFFCInfoSym is not implemented yet on " + "object generation path"); + } }; } // end namespace llvm diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp --- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp @@ -486,6 +486,11 @@ } } + // On AIX, emit bytes for llvm.commandline metadata after .file so that the + // C_INFO symbol is preserved if any csect is kept by the linker. + if (TM.getTargetTriple().isOSBinFormatXCOFF()) + emitModuleCommandLines(M); + GCModuleInfo *MI = getAnalysisIfAvailable(); assert(MI && "AsmPrinter didn't require GCModuleInfo?"); for (const auto &I : *MI) @@ -2330,7 +2335,9 @@ emitModuleIdents(M); // Emit bytes for llvm.commandline metadata. - emitModuleCommandLines(M); + // The command line metadata is emitted earlier on XCOFF. + if (!TM.getTargetTriple().isOSBinFormatXCOFF()) + emitModuleCommandLines(M); // Emit .note.GNU-split-stack and .note.GNU-no-split-stack sections if // split-stack is used. diff --git a/llvm/lib/MC/MCAsmStreamer.cpp b/llvm/lib/MC/MCAsmStreamer.cpp --- a/llvm/lib/MC/MCAsmStreamer.cpp +++ b/llvm/lib/MC/MCAsmStreamer.cpp @@ -36,6 +36,7 @@ #include "llvm/Support/LEB128.h" #include "llvm/Support/MathExtras.h" #include "llvm/Support/Path.h" +#include #include using namespace llvm; @@ -200,6 +201,7 @@ const MCSymbol *Trap, unsigned Lang, unsigned Reason, unsigned FunctionSize, bool hasDebug) override; + void emitXCOFFCInfoSym(StringRef Name, StringRef Metadata) override; void emitELFSize(MCSymbol *Symbol, const MCExpr *Value) override; void emitCommonSymbol(MCSymbol *Symbol, uint64_t Size, @@ -966,6 +968,70 @@ EmitEOL(); } +void MCAsmStreamer::emitXCOFFCInfoSym(StringRef Name, StringRef Metadata) { + const char InfoDirective[] = "\t.info "; + const char *Separator = ", "; + constexpr int WordSize = sizeof(uint32_t); + + // Start by emitting the .info pseudo-op and C_INFO symbol name. + OS << InfoDirective; + PrintQuotedString(Name, OS); + OS << Separator; + + size_t MetadataSize = Metadata.size(); + + // Emit the 4-byte length of the metadata. + OS << format_hex(MetadataSize, 10) << Separator; + + // Nothing left to do if there's no metadata. + if (MetadataSize == 0) { + EmitEOL(); + return; + } + + // Metadata needs to be padded out to an even word size when generating + // assembly because the .info pseudo-op can only generate words of data. We + // apply the same restriction to the object case for consistency, however the + // linker doesn't require padding, so it will only save bytes specified by the + // length and discard any padding. + uint32_t PaddedSize = alignTo(MetadataSize, WordSize); + uint32_t PaddingSize = PaddedSize - MetadataSize; + + // Write out the payload a word at a time. + // + // The assembler has a limit on the number of operands in an expression, + // so we need multiple .info pseudo-ops. We choose a small number of words + // per pseudo-op to keep the assembly readable. + constexpr int WordsPerDirective = 5; + // Force emitting a new directive to keep the first directive purely about the + // name and size of the note. + int WordsBeforeNextDirective = 0; + auto PrintWord = [&](const uint8_t *WordPtr) { + if (WordsBeforeNextDirective-- == 0) { + EmitEOL(); + OS << InfoDirective; + WordsBeforeNextDirective = WordsPerDirective; + } + OS << Separator; + uint32_t Word = llvm::support::endian::read32be(WordPtr); + OS << format_hex(Word, 10); + }; + + size_t Index = 0; + for (; Index + WordSize <= MetadataSize; Index += WordSize) + PrintWord(reinterpret_cast(Metadata.data()) + Index); + + // If there is padding, then we have at least one byte of payload left + // to emit. + if (PaddingSize) { + assert(PaddedSize - Index == WordSize); + std::array LastWord = {0}; + ::memcpy(LastWord.data(), Metadata.data() + Index, MetadataSize - Index); + PrintWord(LastWord.data()); + } + EmitEOL(); +} + void MCAsmStreamer::emitELFSize(MCSymbol *Symbol, const MCExpr *Value) { assert(MAI->hasDotTypeDotSizeDirective()); OS << "\t.size\t"; 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 @@ -1208,6 +1208,11 @@ "XCOFF targets"); } +void MCStreamer::emitXCOFFCInfoSym(StringRef Name, StringRef Metadata) { + llvm_unreachable("emitXCOFFCInfoSym is only supported on" + "XCOFF targets"); +} + void MCStreamer::emitELFSize(MCSymbol *Symbol, const MCExpr *Value) {} void MCStreamer::emitELFSymverDirective(const MCSymbol *OriginalSym, StringRef Name, bool KeepOriginalSym) {} diff --git a/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp b/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp --- a/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp +++ b/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp @@ -290,6 +290,8 @@ bool doFinalization(Module &M) override; void emitTTypeReference(const GlobalValue *GV, unsigned Encoding) override; + + void emitModuleCommandLines(Module &M) override; }; } // end anonymous namespace @@ -2954,6 +2956,26 @@ return new PPCLinuxAsmPrinter(tm, std::move(Streamer)); } +void PPCAIXAsmPrinter::emitModuleCommandLines(Module &M) { + const NamedMDNode *NMD = M.getNamedMetadata("llvm.commandline"); + if (!NMD || !NMD->getNumOperands()) + return; + + std::string S; + raw_string_ostream RSOS(S); + for (unsigned i = 0, e = NMD->getNumOperands(); i != e; ++i) { + const MDNode *N = NMD->getOperand(i); + assert(N->getNumOperands() == 1 && + "llvm.commandline metadata entry can have only one operand"); + const MDString *MDS = cast(N->getOperand(0)); + // Add "@(#)" to support retrieving the command line information with the + // AIX "what" command + RSOS << "@(#)opt " << MDS->getString() << "\n"; + RSOS.write('\0'); + } + OutStreamer->emitXCOFFCInfoSym(".GCC.command.line", RSOS.str()); +} + // Force static initialization. extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializePowerPCAsmPrinter() { TargetRegistry::RegisterAsmPrinter(getThePPC32Target(), diff --git a/llvm/test/CodeGen/PowerPC/aix-command-line-metadata.ll b/llvm/test/CodeGen/PowerPC/aix-command-line-metadata.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/PowerPC/aix-command-line-metadata.ll @@ -0,0 +1,23 @@ +; RUN: llc -mtriple powerpc-ibm-aix-xcoff < %s | \ +; RUN: FileCheck --check-prefix=ASM %s +; RUN: llc -mtriple powerpc64-ibm-aix-xcoff < %s | \ +; RUN: FileCheck --check-prefix=ASM %s + +; RUN: not --crash llc -mtriple powerpc-ibm-aix-xcoff -filetype=obj < %s 2>&1 | \ +; RUN: FileCheck --check-prefix=OBJ %s +; RUN: not --crash llc -mtriple powerpc64-ibm-aix-xcoff -filetype=obj < %s 2>&1 | \ +; RUN: FileCheck --check-prefix=OBJ %s + +; Verify that llvm.commandline metadata is emitted to .info sections and that the +; metadata is padded if necessary. + +; OBJ: LLVM ERROR: emitXCOFFCInfoSym is not implemented yet on object generation path + +; ASM: .info ".GCC.command.line", 0x0000003a, +; ASM: .info , 0x40282329, 0x6f707420, 0x636c616e, 0x67202d63, 0x6f6d6d61, 0x6e64202d +; ASM: .info , 0x6c696e65, 0x0a004028, 0x23296f70, 0x7420736f, 0x6d657468, 0x696e6720 +; ASM: .info , 0x656c7365, 0x20313233, 0x0a000000 + +!llvm.commandline = !{!0, !1} +!0 = !{!"clang -command -line"} +!1 = !{!"something else 123"}