Skip to content

Commit

Permalink
[clang][flang][mlir] Support -frecord-command-line option (#102975)
Browse files Browse the repository at this point in the history
Add support for the -frecord-command-line option that will produce the
llvm.commandline metadata which will eventually be saved in the object
file. This behavior is also supported in clang. Some refactoring of the
code in flang to handle these command line options was carried out. The
corresponding -grecord-command-line option which saves the command line
in the debug information has not yet been enabled for flang.
  • Loading branch information
tarunprabhu authored Sep 20, 2024
1 parent 594efd2 commit b3533a1
Show file tree
Hide file tree
Showing 23 changed files with 245 additions and 59 deletions.
14 changes: 8 additions & 6 deletions clang/include/clang/Driver/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -1994,16 +1994,18 @@ def fparse_all_comments : Flag<["-"], "fparse-all-comments">, Group<f_clang_Grou
Visibility<[ClangOption, CC1Option]>,
MarshallingInfoFlag<LangOpts<"CommentOpts.ParseAllComments">>;
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<f_clang_Group>;
Group<f_Group>,
Visibility<[ClangOption, CLOption, DXCOption, FlangOption]>;
def fno_record_command_line : Flag<["-"], "fno-record-command-line">,
Group<f_clang_Group>;
Group<f_Group>,
Visibility<[ClangOption, CLOption, DXCOption, FlangOption]>;
def : Flag<["-"], "frecord-gcc-switches">, Alias<frecord_command_line>;
def : Flag<["-"], "fno-record-gcc-switches">, Alias<fno_record_command_line>;
def fcommon : Flag<["-"], "fcommon">, Group<f_Group>,
Expand Down Expand Up @@ -7141,6 +7143,9 @@ def mrelocation_model : Separate<["-"], "mrelocation-model">,
NormalizedValues<["Static", "PIC_", "ROPI", "RWPI", "ROPI_RWPI", "DynamicNoPIC"]>,
MarshallingInfoEnum<CodeGenOpts<"RelocationModel">, "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<CodeGenOpts<"RecordCommandLine">>;

} // let Visibility = [CC1Option, CC1AsOption, FC1Option]

Expand All @@ -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<CodeGenOpts<"DwarfDebugFlags">>;
def record_command_line : Separate<["-"], "record-command-line">,
HelpText<"The string to embed in the .LLVM.command.line section.">,
MarshallingInfoString<CodeGenOpts<"RecordCommandLine">>;
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"]>,
Expand Down
51 changes: 6 additions & 45 deletions clang/lib/Driver/ToolChains/Clang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<char> &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
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
}
Expand Down
59 changes: 59 additions & 0 deletions clang/lib/Driver/ToolChains/CommonArgs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2960,3 +2960,62 @@ void tools::addMCModel(const Driver &D, const llvm::opt::ArgList &Args,
}
}
}

void tools::escapeSpacesAndBackslashes(const char *Arg,
llvm::SmallVectorImpl<char> &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;
}
25 changes: 25 additions & 0 deletions clang/lib/Driver/ToolChains/CommonArgs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<char> &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
Expand Down
14 changes: 14 additions & 0 deletions clang/lib/Driver/ToolChains/Flang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions flang/include/flang/Frontend/CodeGenOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ class CodeGenOptions : public CodeGenOptionsBase {
/// The directory where temp files are stored if specified by -save-temps
std::optional<std::string> SaveTempsDir;

/// The string containing the commandline for the llvm.commandline metadata.
std::optional<std::string> RecordCommandLine;

/// The name of the file to which the backend should save YAML optimization
/// records.
std::string OptRecordFile;
Expand Down
12 changes: 9 additions & 3 deletions flang/include/flang/Lower/Bridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -65,11 +67,13 @@ class LoweringBridge {
const Fortran::lower::LoweringOptions &loweringOptions,
const std::vector<Fortran::lower::EnvironmentDefault> &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);
}

//===--------------------------------------------------------------------===//
Expand Down Expand Up @@ -148,7 +152,9 @@ class LoweringBridge {
const Fortran::lower::LoweringOptions &loweringOptions,
const std::vector<Fortran::lower::EnvironmentDefault> &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;

Expand Down
6 changes: 6 additions & 0 deletions flang/include/flang/Optimizer/Dialect/Support/FIRContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -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=<string>`
/// command-line option.
Expand Down
6 changes: 6 additions & 0 deletions flang/lib/Frontend/CompilerInvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
2 changes: 1 addition & 1 deletion flang/lib/Frontend/FrontendActions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<mlir::ModuleOp>(lb.getModule());
Expand Down
8 changes: 6 additions & 2 deletions flang/lib/Lower/Bridge.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6064,7 +6064,9 @@ Fortran::lower::LoweringBridge::LoweringBridge(
const Fortran::lower::LoweringOptions &loweringOptions,
const std::vector<Fortran::lower::EnvironmentDefault> &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},
Expand Down Expand Up @@ -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(
Expand Down
16 changes: 16 additions & 0 deletions flang/lib/Optimizer/Dialect/Support/FIRContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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::StringAttr>(
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")
Expand Down
16 changes: 16 additions & 0 deletions flang/test/Driver/frecord-command-line.f90
Original file line number Diff line number Diff line change
@@ -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
9 changes: 9 additions & 0 deletions flang/test/Lower/record-command-line.f90
Original file line number Diff line number Diff line change
@@ -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"

1 change: 1 addition & 0 deletions flang/tools/bbc/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,5 @@ FortranParser
FortranEvaluate
FortranSemantics
FortranLower
flangFrontend
)
Loading

0 comments on commit b3533a1

Please sign in to comment.