diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index aa3ae92fb6ae78..021230ceed4cb4 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -1994,16 +1994,18 @@ def fparse_all_comments : Flag<["-"], "fparse-all-comments">, Group, MarshallingInfoFlag>; def frecord_command_line : Flag<["-"], "frecord-command-line">, - DocBrief<[{Generate a section named ".GCC.command.line" containing the clang + DocBrief<[{Generate a section named ".GCC.command.line" containing the driver command-line. After linking, the section may contain multiple command lines, which will be individually terminated by null bytes. Separate arguments within a command line are combined with spaces; spaces and backslashes within an argument are escaped with backslashes. This format differs from the format of the equivalent section produced by GCC with the -frecord-gcc-switches flag. This option is currently only supported on ELF targets.}]>, - Group; + Group, + Visibility<[ClangOption, CLOption, DXCOption, FlangOption]>; def fno_record_command_line : Flag<["-"], "fno-record-command-line">, - Group; + Group, + Visibility<[ClangOption, CLOption, DXCOption, FlangOption]>; def : Flag<["-"], "frecord-gcc-switches">, Alias; def : Flag<["-"], "fno-record-gcc-switches">, Alias; def fcommon : Flag<["-"], "fcommon">, Group, @@ -7141,6 +7143,9 @@ def mrelocation_model : Separate<["-"], "mrelocation-model">, NormalizedValues<["Static", "PIC_", "ROPI", "RWPI", "ROPI_RWPI", "DynamicNoPIC"]>, MarshallingInfoEnum, "PIC_">; def debug_info_kind_EQ : Joined<["-"], "debug-info-kind=">; +def record_command_line : Separate<["-"], "record-command-line">, + HelpText<"The string to embed in the .LLVM.command.line section.">, + MarshallingInfoString>; } // let Visibility = [CC1Option, CC1AsOption, FC1Option] @@ -7161,9 +7166,6 @@ def debugger_tuning_EQ : Joined<["-"], "debugger-tuning=">, def dwarf_debug_flags : Separate<["-"], "dwarf-debug-flags">, HelpText<"The string to embed in the Dwarf debug flags record.">, MarshallingInfoString>; -def record_command_line : Separate<["-"], "record-command-line">, - HelpText<"The string to embed in the .LLVM.command.line section.">, - MarshallingInfoString>; def compress_debug_sections_EQ : Joined<["-", "--"], "compress-debug-sections=">, HelpText<"DWARF debug sections compression type">, Values<"none,zlib,zstd">, NormalizedValuesScope<"llvm::DebugCompressionType">, NormalizedValues<["None", "Zlib", "Zstd"]>, diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index c00df5f5bc729c..7e83ec0e70cbcf 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -94,24 +94,6 @@ static void CheckCodeGenerationOptions(const Driver &D, const ArgList &Args) { << "-static"; } -// Add backslashes to escape spaces and other backslashes. -// This is used for the space-separated argument list specified with -// the -dwarf-debug-flags option. -static void EscapeSpacesAndBackslashes(const char *Arg, - SmallVectorImpl &Res) { - for (; *Arg; ++Arg) { - switch (*Arg) { - default: - break; - case ' ': - case '\\': - Res.push_back('\\'); - break; - } - Res.push_back(*Arg); - } -} - /// Apply \a Work on the current tool chain \a RegularToolChain and any other /// offloading tool chain that is associated with the current action \a JA. static void @@ -7702,31 +7684,10 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, // Also record command line arguments into the debug info if // -grecord-gcc-switches options is set on. // By default, -gno-record-gcc-switches is set on and no recording. - auto GRecordSwitches = - Args.hasFlag(options::OPT_grecord_command_line, - options::OPT_gno_record_command_line, false); - auto FRecordSwitches = - Args.hasFlag(options::OPT_frecord_command_line, - options::OPT_fno_record_command_line, false); - if (FRecordSwitches && !Triple.isOSBinFormatELF() && - !Triple.isOSBinFormatXCOFF() && !Triple.isOSBinFormatMachO()) - D.Diag(diag::err_drv_unsupported_opt_for_target) - << Args.getLastArg(options::OPT_frecord_command_line)->getAsString(Args) - << TripleStr; - if (TC.UseDwarfDebugFlags() || GRecordSwitches || FRecordSwitches) { - ArgStringList OriginalArgs; - for (const auto &Arg : Args) - Arg->render(Args, OriginalArgs); - - SmallString<256> Flags; - EscapeSpacesAndBackslashes(Exec, Flags); - for (const char *OriginalArg : OriginalArgs) { - SmallString<128> EscapedArg; - EscapeSpacesAndBackslashes(OriginalArg, EscapedArg); - Flags += " "; - Flags += EscapedArg; - } - auto FlagsArgString = Args.MakeArgString(Flags); + auto GRecordSwitches = false; + auto FRecordSwitches = false; + if (shouldRecordCommandLine(TC, Args, FRecordSwitches, GRecordSwitches)) { + auto FlagsArgString = renderEscapedCommandLine(TC, Args); if (TC.UseDwarfDebugFlags() || GRecordSwitches) { CmdArgs.push_back("-dwarf-debug-flags"); CmdArgs.push_back(FlagsArgString); @@ -8726,10 +8687,10 @@ void ClangAs::ConstructJob(Compilation &C, const JobAction &JA, SmallString<256> Flags; const char *Exec = getToolChain().getDriver().getClangProgramPath(); - EscapeSpacesAndBackslashes(Exec, Flags); + escapeSpacesAndBackslashes(Exec, Flags); for (const char *OriginalArg : OriginalArgs) { SmallString<128> EscapedArg; - EscapeSpacesAndBackslashes(OriginalArg, EscapedArg); + escapeSpacesAndBackslashes(OriginalArg, EscapedArg); Flags += " "; Flags += EscapedArg; } diff --git a/clang/lib/Driver/ToolChains/CommonArgs.cpp b/clang/lib/Driver/ToolChains/CommonArgs.cpp index 043d9e48764439..102e5231d31608 100644 --- a/clang/lib/Driver/ToolChains/CommonArgs.cpp +++ b/clang/lib/Driver/ToolChains/CommonArgs.cpp @@ -2960,3 +2960,62 @@ void tools::addMCModel(const Driver &D, const llvm::opt::ArgList &Args, } } } + +void tools::escapeSpacesAndBackslashes(const char *Arg, + llvm::SmallVectorImpl &Res) { + for (; *Arg; ++Arg) { + switch (*Arg) { + default: + break; + case ' ': + case '\\': + Res.push_back('\\'); + break; + } + Res.push_back(*Arg); + } +} + +const char *tools::renderEscapedCommandLine(const ToolChain &TC, + const llvm::opt::ArgList &Args) { + const Driver &D = TC.getDriver(); + const char *Exec = D.getClangProgramPath(); + + llvm::opt::ArgStringList OriginalArgs; + for (const auto &Arg : Args) + Arg->render(Args, OriginalArgs); + + llvm::SmallString<256> Flags; + escapeSpacesAndBackslashes(Exec, Flags); + for (const char *OriginalArg : OriginalArgs) { + llvm::SmallString<128> EscapedArg; + escapeSpacesAndBackslashes(OriginalArg, EscapedArg); + Flags += " "; + Flags += EscapedArg; + } + + return Args.MakeArgString(Flags); +} + +bool tools::shouldRecordCommandLine(const ToolChain &TC, + const llvm::opt::ArgList &Args, + bool &FRecordCommandLine, + bool &GRecordCommandLine) { + const Driver &D = TC.getDriver(); + const llvm::Triple &Triple = TC.getEffectiveTriple(); + const std::string &TripleStr = Triple.getTriple(); + + FRecordCommandLine = + Args.hasFlag(options::OPT_frecord_command_line, + options::OPT_fno_record_command_line, false); + GRecordCommandLine = + Args.hasFlag(options::OPT_grecord_command_line, + options::OPT_gno_record_command_line, false); + if (FRecordCommandLine && !Triple.isOSBinFormatELF() && + !Triple.isOSBinFormatXCOFF() && !Triple.isOSBinFormatMachO()) + D.Diag(diag::err_drv_unsupported_opt_for_target) + << Args.getLastArg(options::OPT_frecord_command_line)->getAsString(Args) + << TripleStr; + + return FRecordCommandLine || TC.UseDwarfDebugFlags() || GRecordCommandLine; +} diff --git a/clang/lib/Driver/ToolChains/CommonArgs.h b/clang/lib/Driver/ToolChains/CommonArgs.h index 8695d3fe5b55b8..e9b42bb872ed54 100644 --- a/clang/lib/Driver/ToolChains/CommonArgs.h +++ b/clang/lib/Driver/ToolChains/CommonArgs.h @@ -233,6 +233,31 @@ void addMCModel(const Driver &D, const llvm::opt::ArgList &Args, const llvm::Reloc::Model &RelocationModel, llvm::opt::ArgStringList &CmdArgs); +/// Add backslashes to escape spaces and other backslashes. +/// This is used for the space-separated argument list specified with +/// the -dwarf-debug-flags option. +void escapeSpacesAndBackslashes(const char *Arg, + llvm::SmallVectorImpl &Res); + +/// Join the args in the given ArgList, escape spaces and backslashes and +/// return the joined string. This is used when saving the command line as a +/// result of using either the -frecord-command-line or -grecord-command-line +/// options. The lifetime of the returned c-string will match that of the Args +/// argument. +const char *renderEscapedCommandLine(const ToolChain &TC, + const llvm::opt::ArgList &Args); + +/// Check if the command line should be recorded in the object file. This is +/// done if either -frecord-command-line or -grecord-command-line options have +/// been passed. This also does some error checking since -frecord-command-line +/// is currently only supported on ELF platforms. The last two boolean +/// arguments are out parameters and will be set depending on the command +/// line options that were passed. +bool shouldRecordCommandLine(const ToolChain &TC, + const llvm::opt::ArgList &Args, + bool &FRecordCommandLine, + bool &GRecordCommandLine); + } // end namespace tools } // end namespace driver } // end namespace clang diff --git a/clang/lib/Driver/ToolChains/Flang.cpp b/clang/lib/Driver/ToolChains/Flang.cpp index 6ce79d27e98c48..65c29b3dd7f658 100644 --- a/clang/lib/Driver/ToolChains/Flang.cpp +++ b/clang/lib/Driver/ToolChains/Flang.cpp @@ -885,6 +885,20 @@ void Flang::ConstructJob(Compilation &C, const JobAction &JA, addDashXForInput(Args, Input, CmdArgs); + bool FRecordCmdLine = false; + bool GRecordCmdLine = false; + if (shouldRecordCommandLine(TC, Args, FRecordCmdLine, GRecordCmdLine)) { + const char *CmdLine = renderEscapedCommandLine(TC, Args); + if (FRecordCmdLine) { + CmdArgs.push_back("-record-command-line"); + CmdArgs.push_back(CmdLine); + } + if (TC.UseDwarfDebugFlags() || GRecordCmdLine) { + CmdArgs.push_back("-dwarf-debug-flags"); + CmdArgs.push_back(CmdLine); + } + } + CmdArgs.push_back(Input.getFilename()); // TODO: Replace flang-new with flang once the new driver replaces the diff --git a/flang/include/flang/Frontend/CodeGenOptions.h b/flang/include/flang/Frontend/CodeGenOptions.h index ac7fcbcba4f747..f19943335737b9 100644 --- a/flang/include/flang/Frontend/CodeGenOptions.h +++ b/flang/include/flang/Frontend/CodeGenOptions.h @@ -63,6 +63,9 @@ class CodeGenOptions : public CodeGenOptionsBase { /// The directory where temp files are stored if specified by -save-temps std::optional SaveTempsDir; + /// The string containing the commandline for the llvm.commandline metadata. + std::optional RecordCommandLine; + /// The name of the file to which the backend should save YAML optimization /// records. std::string OptRecordFile; diff --git a/flang/include/flang/Lower/Bridge.h b/flang/include/flang/Lower/Bridge.h index 4379ed512cdf0a..8ea5ed52e28218 100644 --- a/flang/include/flang/Lower/Bridge.h +++ b/flang/include/flang/Lower/Bridge.h @@ -14,6 +14,8 @@ #define FORTRAN_LOWER_BRIDGE_H #include "flang/Common/Fortran.h" +#include "flang/Frontend/CodeGenOptions.h" +#include "flang/Frontend/TargetOptions.h" #include "flang/Lower/AbstractConverter.h" #include "flang/Lower/EnvironmentDefault.h" #include "flang/Lower/LoweringOptions.h" @@ -65,11 +67,13 @@ class LoweringBridge { const Fortran::lower::LoweringOptions &loweringOptions, const std::vector &envDefaults, const Fortran::common::LanguageFeatureControl &languageFeatures, - const llvm::TargetMachine &targetMachine, llvm::StringRef tuneCPU) { + const llvm::TargetMachine &targetMachine, + const Fortran::frontend::TargetOptions &targetOptions, + const Fortran::frontend::CodeGenOptions &codeGenOptions) { return LoweringBridge(ctx, semanticsContext, defaultKinds, intrinsics, targetCharacteristics, allCooked, triple, kindMap, loweringOptions, envDefaults, languageFeatures, - targetMachine, tuneCPU); + targetMachine, targetOptions, codeGenOptions); } //===--------------------------------------------------------------------===// @@ -148,7 +152,9 @@ class LoweringBridge { const Fortran::lower::LoweringOptions &loweringOptions, const std::vector &envDefaults, const Fortran::common::LanguageFeatureControl &languageFeatures, - const llvm::TargetMachine &targetMachine, const llvm::StringRef tuneCPU); + const llvm::TargetMachine &targetMachine, + const Fortran::frontend::TargetOptions &targetOptions, + const Fortran::frontend::CodeGenOptions &codeGenOptions); LoweringBridge() = delete; LoweringBridge(const LoweringBridge &) = delete; diff --git a/flang/include/flang/Optimizer/Dialect/Support/FIRContext.h b/flang/include/flang/Optimizer/Dialect/Support/FIRContext.h index e45011c8e02a33..2df14f83c11e17 100644 --- a/flang/include/flang/Optimizer/Dialect/Support/FIRContext.h +++ b/flang/include/flang/Optimizer/Dialect/Support/FIRContext.h @@ -77,6 +77,12 @@ void setIdent(mlir::ModuleOp mod, llvm::StringRef ident); /// Get the compiler identifier from the Module. llvm::StringRef getIdent(mlir::ModuleOp mod); +/// Set the command line used in this invocation. +void setCommandline(mlir::ModuleOp mod, llvm::StringRef cmdLine); + +/// Get the command line used in this invocation. +llvm::StringRef getCommandline(mlir::ModuleOp mod); + /// Helper for determining the target from the host, etc. Tools may use this /// function to provide a consistent interpretation of the `--target=` /// command-line option. diff --git a/flang/lib/Frontend/CompilerInvocation.cpp b/flang/lib/Frontend/CompilerInvocation.cpp index 90c327546198b5..8441c7d8d2e9bc 100644 --- a/flang/lib/Frontend/CompilerInvocation.cpp +++ b/flang/lib/Frontend/CompilerInvocation.cpp @@ -348,6 +348,12 @@ static void parseCodeGenArgs(Fortran::frontend::CodeGenOptions &opts, if (auto *a = args.getLastArg(clang::driver::options::OPT_save_temps_EQ)) opts.SaveTempsDir = a->getValue(); + // -record-command-line option. + if (const llvm::opt::Arg *a = + args.getLastArg(clang::driver::options::OPT_record_command_line)) { + opts.RecordCommandLine = a->getValue(); + } + // -mlink-builtin-bitcode for (auto *a : args.filtered(clang::driver::options::OPT_mlink_builtin_bitcode)) diff --git a/flang/lib/Frontend/FrontendActions.cpp b/flang/lib/Frontend/FrontendActions.cpp index 267c3ceb44f33e..62a041c8202fcb 100644 --- a/flang/lib/Frontend/FrontendActions.cpp +++ b/flang/lib/Frontend/FrontendActions.cpp @@ -302,7 +302,7 @@ bool CodeGenAction::beginSourceFileAction() { kindMap, ci.getInvocation().getLoweringOpts(), ci.getInvocation().getFrontendOpts().envDefaults, ci.getInvocation().getFrontendOpts().features, targetMachine, - ci.getInvocation().getTargetOpts().cpuToTuneFor); + ci.getInvocation().getTargetOpts(), ci.getInvocation().getCodeGenOpts()); // Fetch module from lb, so we can set mlirModule = std::make_unique(lb.getModule()); diff --git a/flang/lib/Lower/Bridge.cpp b/flang/lib/Lower/Bridge.cpp index ebcb7613969661..68dca219428280 100644 --- a/flang/lib/Lower/Bridge.cpp +++ b/flang/lib/Lower/Bridge.cpp @@ -6064,7 +6064,9 @@ Fortran::lower::LoweringBridge::LoweringBridge( const Fortran::lower::LoweringOptions &loweringOptions, const std::vector &envDefaults, const Fortran::common::LanguageFeatureControl &languageFeatures, - const llvm::TargetMachine &targetMachine, const llvm::StringRef tuneCPU) + const llvm::TargetMachine &targetMachine, + const Fortran::frontend::TargetOptions &targetOpts, + const Fortran::frontend::CodeGenOptions &cgOpts) : semanticsContext{semanticsContext}, defaultKinds{defaultKinds}, intrinsics{intrinsics}, targetCharacteristics{targetCharacteristics}, cooked{&cooked}, context{context}, kindMap{kindMap}, @@ -6121,11 +6123,13 @@ Fortran::lower::LoweringBridge::LoweringBridge( fir::setTargetTriple(*module.get(), triple); fir::setKindMapping(*module.get(), kindMap); fir::setTargetCPU(*module.get(), targetMachine.getTargetCPU()); - fir::setTuneCPU(*module.get(), tuneCPU); + fir::setTuneCPU(*module.get(), targetOpts.cpuToTuneFor); fir::setTargetFeatures(*module.get(), targetMachine.getTargetFeatureString()); fir::support::setMLIRDataLayout(*module.get(), targetMachine.createDataLayout()); fir::setIdent(*module.get(), Fortran::common::getFlangFullVersion()); + if (cgOpts.RecordCommandLine) + fir::setCommandline(*module.get(), *cgOpts.RecordCommandLine); } void Fortran::lower::genCleanUpInRegionIfAny( diff --git a/flang/lib/Optimizer/Dialect/Support/FIRContext.cpp b/flang/lib/Optimizer/Dialect/Support/FIRContext.cpp index 5bd8e2d4336361..01c0be66d1ecc3 100644 --- a/flang/lib/Optimizer/Dialect/Support/FIRContext.cpp +++ b/flang/lib/Optimizer/Dialect/Support/FIRContext.cpp @@ -130,6 +130,22 @@ llvm::StringRef fir::getIdent(mlir::ModuleOp mod) { return {}; } +void fir::setCommandline(mlir::ModuleOp mod, llvm::StringRef cmdLine) { + if (cmdLine.empty()) + return; + + mlir::MLIRContext *ctx = mod.getContext(); + mod->setAttr(mlir::LLVM::LLVMDialect::getCommandlineAttrName(), + mlir::StringAttr::get(ctx, cmdLine)); +} + +llvm::StringRef fir::getCommandline(mlir::ModuleOp mod) { + if (auto attr = mod->getAttrOfType( + mlir::LLVM::LLVMDialect::getCommandlineAttrName())) + return attr; + return {}; +} + std::string fir::determineTargetTriple(llvm::StringRef triple) { // Treat "" or "default" as stand-ins for the default machine. if (triple.empty() || triple == "default") diff --git a/flang/test/Driver/frecord-command-line.f90 b/flang/test/Driver/frecord-command-line.f90 new file mode 100644 index 00000000000000..bc4ce79e4a51c3 --- /dev/null +++ b/flang/test/Driver/frecord-command-line.f90 @@ -0,0 +1,16 @@ +! This only checks that the command line is correctly passed on to the +! -record-command-line option FC1 option and that the latter does not complain +! about anything. The correct lowering to a module attribute and beyond will +! be checked in other tests. +! +! RUN: %flang -### -target x86_64-unknown-linux -frecord-command-line %s 2>&1 | FileCheck --check-prefix=CHECK-RECORD %s +! RUN: %flang -### -target x86_64-unknown-macosx -frecord-command-line %s 2>&1 | FileCheck --check-prefix=CHECK-RECORD %s +! RUN: not %flang -### -target x86_64-unknown-windows -frecord-command-line %s 2>&1 | FileCheck --check-prefix=CHECK-RECORD-ERROR %s + +! RUN: %flang -### -target x86_64-unknown-linux -fno-record-command-line %s 2>&1 | FileCheck --check-prefix=CHECK-NO-RECORD %s +! RUN: %flang -### -target x86_64-unknown-macosx -fno-record-command-line %s 2>&1 | FileCheck --check-prefix=CHECK-NO-RECORD %s +! RUN: %flang -### -target x86_64-unknown-windows -fno-record-command-line %s 2>&1 | FileCheck --check-prefix=CHECK-NO-RECORD %s + +! CHECK-RECORD: "-record-command-line" +! CHECK-NO-RECORD-NOT: "-record-command-line" +! CHECK-RECORD-ERROR: error: unsupported option '-frecord-command-line' for target diff --git a/flang/test/Lower/record-command-line.f90 b/flang/test/Lower/record-command-line.f90 new file mode 100644 index 00000000000000..a7a25bb9e8725c --- /dev/null +++ b/flang/test/Lower/record-command-line.f90 @@ -0,0 +1,9 @@ +! The actual command line is recorded by the frontend and passed on to FC1 as +! the argument to -record-command-line, so in this test, we just match against +! some string with spaces that mimics what a hypothetical command line. + +! RUN: %flang_fc1 -record-command-line "exec -o infile" %s -emit-fir -o - | FileCheck %s + +! CHECK: module attributes { +! CHECK-SAME: llvm.commandline = "exec -o infile" + diff --git a/flang/tools/bbc/CMakeLists.txt b/flang/tools/bbc/CMakeLists.txt index 69316d4dc61de3..7db3b5d40c7c94 100644 --- a/flang/tools/bbc/CMakeLists.txt +++ b/flang/tools/bbc/CMakeLists.txt @@ -35,4 +35,5 @@ FortranParser FortranEvaluate FortranSemantics FortranLower +flangFrontend ) diff --git a/flang/tools/bbc/bbc.cpp b/flang/tools/bbc/bbc.cpp index dcff4503f16571..935fae2fc96a70 100644 --- a/flang/tools/bbc/bbc.cpp +++ b/flang/tools/bbc/bbc.cpp @@ -18,6 +18,7 @@ #include "flang/Common/OpenMP-features.h" #include "flang/Common/Version.h" #include "flang/Common/default-kinds.h" +#include "flang/Frontend/CodeGenOptions.h" #include "flang/Frontend/TargetOptions.h" #include "flang/Lower/Bridge.h" #include "flang/Lower/PFTBuilder.h" @@ -373,12 +374,13 @@ static llvm::LogicalResult convertFortranSourceToMLIR( loweringOptions.setLowerToHighLevelFIR(useHLFIR || emitHLFIR); loweringOptions.setNSWOnLoopVarInc(setNSW); std::vector envDefaults = {}; - constexpr const char *tuneCPU = ""; + Fortran::frontend::TargetOptions targetOpts; + Fortran::frontend::CodeGenOptions cgOpts; auto burnside = Fortran::lower::LoweringBridge::create( ctx, semanticsContext, defKinds, semanticsContext.intrinsics(), semanticsContext.targetCharacteristics(), parsing.allCooked(), targetTriple, kindMap, loweringOptions, envDefaults, - semanticsContext.languageFeatures(), targetMachine, tuneCPU); + semanticsContext.languageFeatures(), targetMachine, targetOpts, cgOpts); mlir::ModuleOp mlirModule = burnside.getModule(); if (enableOpenMP) { if (enableOpenMPGPU && !enableOpenMPDevice) { diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMDialect.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMDialect.td index edcc34461f2f26..27a2b418aadb2a 100644 --- a/mlir/include/mlir/Dialect/LLVMIR/LLVMDialect.td +++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMDialect.td @@ -33,6 +33,7 @@ def LLVM_Dialect : Dialect { static StringRef getAliasScopesAttrName() { return "alias_scopes"; } static StringRef getAccessGroupsAttrName() { return "access_groups"; } static StringRef getIdentAttrName() { return "llvm.ident"; } + static StringRef getCommandlineAttrName() { return "llvm.commandline"; } /// Names of llvm parameter attributes. static StringRef getAlignAttrName() { return "llvm.align"; } diff --git a/mlir/include/mlir/Target/LLVMIR/ModuleImport.h b/mlir/include/mlir/Target/LLVMIR/ModuleImport.h index 11f62c283d6a96..d32aed69cd90e8 100644 --- a/mlir/include/mlir/Target/LLVMIR/ModuleImport.h +++ b/mlir/include/mlir/Target/LLVMIR/ModuleImport.h @@ -199,6 +199,10 @@ class ModuleImport { /// Converts !llvm.ident metadata to the llvm.ident LLVM ModuleOp attribute. LogicalResult convertIdentMetadata(); + /// Converts !llvm.commandline metadata to the llvm.commandline LLVM ModuleOp + /// attribute. + LogicalResult convertCommandlineMetadata(); + /// Converts all LLVM metadata nodes that translate to attributes such as /// alias analysis or access group metadata, and builds a map from the /// metadata nodes to the converted attributes. diff --git a/mlir/include/mlir/Target/LLVMIR/ModuleTranslation.h b/mlir/include/mlir/Target/LLVMIR/ModuleTranslation.h index 3c85338bc642f6..ffeeeae57ae952 100644 --- a/mlir/include/mlir/Target/LLVMIR/ModuleTranslation.h +++ b/mlir/include/mlir/Target/LLVMIR/ModuleTranslation.h @@ -332,6 +332,9 @@ class ModuleTranslation { /// Process the ident LLVM Metadata, if it exists. LogicalResult createIdentMetadata(); + /// Process the llvm.commandline LLVM Metadata, if it exists. + LogicalResult createCommandlineMetadata(); + /// Translates dialect attributes attached to the given operation. LogicalResult convertDialectAttributes(Operation *op, diff --git a/mlir/lib/Target/LLVMIR/ModuleImport.cpp b/mlir/lib/Target/LLVMIR/ModuleImport.cpp index 21f2050cbceb9c..57dc0bf27d8c27 100644 --- a/mlir/lib/Target/LLVMIR/ModuleImport.cpp +++ b/mlir/lib/Target/LLVMIR/ModuleImport.cpp @@ -535,6 +535,23 @@ LogicalResult ModuleImport::convertIdentMetadata() { return success(); } +LogicalResult ModuleImport::convertCommandlineMetadata() { + for (const llvm::NamedMDNode &nmd : llvmModule->named_metadata()) { + // llvm.commandline should have a single operand. That operand is itself an + // MDNode with a single string operand. + if (nmd.getName() != LLVMDialect::getCommandlineAttrName()) + continue; + + if (nmd.getNumOperands() == 1) + if (auto *md = dyn_cast(nmd.getOperand(0))) + if (md->getNumOperands() == 1) + if (auto *mdStr = dyn_cast(md->getOperand(0))) + mlirModule->setAttr(LLVMDialect::getCommandlineAttrName(), + builder.getStringAttr(mdStr->getString())); + } + return success(); +} + LogicalResult ModuleImport::convertMetadata() { OpBuilder::InsertionGuard guard(builder); builder.setInsertionPointToEnd(mlirModule.getBody()); @@ -565,6 +582,8 @@ LogicalResult ModuleImport::convertMetadata() { return failure(); if (failed(convertIdentMetadata())) return failure(); + if (failed(convertCommandlineMetadata())) + return failure(); return success(); } diff --git a/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp b/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp index fcb329eb7a92c1..3c6fbe64dc3184 100644 --- a/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp +++ b/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp @@ -1830,6 +1830,21 @@ LogicalResult ModuleTranslation::createIdentMetadata() { return success(); } +LogicalResult ModuleTranslation::createCommandlineMetadata() { + if (auto attr = mlirModule->getAttrOfType( + LLVMDialect::getCommandlineAttrName())) { + StringRef cmdLine = attr; + llvm::LLVMContext &ctx = llvmModule->getContext(); + llvm::NamedMDNode *nmd = llvmModule->getOrInsertNamedMetadata( + LLVMDialect::getCommandlineAttrName()); + llvm::MDNode *md = + llvm::MDNode::get(ctx, llvm::MDString::get(ctx, cmdLine)); + nmd->addOperand(md); + } + + return success(); +} + void ModuleTranslation::setLoopMetadata(Operation *op, llvm::Instruction *inst) { LoopAnnotationAttr attr = @@ -1983,6 +1998,8 @@ mlir::translateModuleToLLVMIR(Operation *module, llvm::LLVMContext &llvmContext, return nullptr; if (failed(translator.createIdentMetadata())) return nullptr; + if (failed(translator.createCommandlineMetadata())) + return nullptr; // Convert other top-level operations if possible. for (Operation &o : getModuleBody(module).getOperations()) { diff --git a/mlir/test/Target/LLVMIR/Import/commandline.ll b/mlir/test/Target/LLVMIR/Import/commandline.ll new file mode 100644 index 00000000000000..f31aec8cc5aac1 --- /dev/null +++ b/mlir/test/Target/LLVMIR/Import/commandline.ll @@ -0,0 +1,6 @@ +; RUN: mlir-translate -import-llvm -split-input-file %s | FileCheck %s + +; CHECK: module attributes { +; CHECK-SAME: llvm.commandline = "exec -o infile" +!llvm.commandline = !{!0} +!0 = !{!"exec -o infile"} diff --git a/mlir/test/Target/LLVMIR/commandline.mlir b/mlir/test/Target/LLVMIR/commandline.mlir new file mode 100644 index 00000000000000..817a5ac2164e9f --- /dev/null +++ b/mlir/test/Target/LLVMIR/commandline.mlir @@ -0,0 +1,6 @@ +// RUN: mlir-translate -mlir-to-llvmir %s | FileCheck %s + +// CHECK: !llvm.commandline = !{![[S:[0-9]+]]} +// CHECK: ![[S]] = !{!"exec -o infile"} +module attributes {llvm.commandline = "exec -o infile"} { +}