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 @@ -7108,7 +7108,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 @@ -485,6 +485,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) @@ -2329,7 +2334,8 @@ emitModuleIdents(M); // Emit bytes for llvm.commandline metadata. - emitModuleCommandLines(M); + 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 @@ -201,6 +201,8 @@ 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, Align ByteAlignment) override; @@ -965,6 +967,75 @@ EmitEOL(); } +void MCAsmStreamer::emitXCOFFCInfoSym(StringRef Name, StringRef Metadata) { + const char *InfoDirective = "\t.info "; + + // Start by emitting the .info pseudo-op and C_INFO symbol name + OS << InfoDirective; + PrintQuotedString(Name, OS); + OS << ", "; + + // Metadata needs to be padded out to an even word size. + size_t MetadataSize = Metadata.size(); + uint32_t MetadataPaddingSize = 3 - (MetadataSize - 1) % 4; + + // Length of the metadata with padding + uint32_t Length = MetadataSize + MetadataPaddingSize; + OS << format_hex(uint32_t(Length), 10) << ","; + EmitEOL(); + + // Return the remaining bytes padded with 0s. + auto GetLastWord = [](const uint8_t *Data, + uint32_t PaddingBytes) -> uint32_t { + uint32_t glw = 0; + switch (PaddingBytes) { + case 1: + glw |= Data[2] << 8; + LLVM_FALLTHROUGH; + case 2: + glw |= Data[1] << 16; + LLVM_FALLTHROUGH; + case 3: + glw |= Data[0] << 24; + return glw; + case 4: + // A special case which means a whole word of padding is needed. + return 0u; + } + report_fatal_error("Unexpected count of padding bytes."); + }; + + // 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. + size_t Index = 0; + while (Index + 4 <= MetadataSize) { + const char *Separator = ", "; + OS << InfoDirective; + for (size_t End = std::min(Index + 20, MetadataSize); Index + 4 <= End; + Index += 4) { + OS << Separator; + + uint32_t NextWord = + llvm::support::endian::read32be(Metadata.data() + Index); + OS << format_hex(NextWord, 10); + } + EmitEOL(); + } + + // If there is padding, then we have at least one byte of payload left + // to emit. + if (MetadataPaddingSize) { + uint32_t LastWord = GetLastWord((const uint8_t *)Metadata.data() + Index, + MetadataPaddingSize); + OS << InfoDirective << ", "; + OS << format_hex(LastWord, 10); + 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 @@ -1204,6 +1204,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 @@ -289,6 +289,8 @@ bool doFinalization(Module &M) override; void emitTTypeReference(const GlobalValue *GV, unsigned Encoding) override; + + void emitModuleCommandLines(Module &M) override; }; } // end anonymous namespace @@ -2964,3 +2966,23 @@ TargetRegistry::RegisterAsmPrinter(getThePPC64LETarget(), createPPCAsmPrinterPass); } + +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 << "@(#)" << MDS->getString(); + RSOS.write('\0'); + } + OutStreamer->emitXCOFFCInfoSym(".GCC.command.line", RSOS.str()); +} 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,25 @@ +; 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", 0x00000030, +; ASM: .info , 0x40282329, 0x636c616e, 0x67202d63, 0x6f6d6d61, 0x6e64202d +; ASM: .info , 0x6c696e65, 0x00402823, 0x29736f6d, 0x65746869, 0x6e672065 +; ASM: .info , 0x6c736531 +; Trailing padding: +; ASM: .info , 0x32330000 + +!llvm.commandline = !{!0, !1} +!0 = !{!"clang -command -line"} +!1 = !{!"something else123"}