[C++20][Modules][Driver][HU 2/N] Add fmodule-header, fmodule-header=

These command-line flags are alternates to providing the -x
c++-*-header indicators that we are building a header unit.

Act on fmodule-header= for headers on the c/l:

If we have x.hh -fmodule-header, then we should treat that header
as a header unit input (equivalent to -xc++-header-unit-header x.hh).

Likewise, for fmodule-header={user,system} the source should be now
recognised as a header unit input (since this can affect the job list
that we need).

It's not practical to recognise a header without any suffix so
-fmodule-header=system foo isn't going to happen. Although
-fmodule-header=system foo.hh will work OK.  However we can make it
work if the user indicates that the item without a suffix is a valid
header. (so -fmodule-header=system -xc++-header vector)

Differential Revision: https://reviews.llvm.org/D121589
This commit is contained in:
Iain Sandoe 2020-08-20 16:18:57 +01:00
parent fc760c0260
commit 4c4ff004a2
5 changed files with 143 additions and 11 deletions

View File

@ -1176,6 +1176,14 @@ Validate the system headers that a module depends on when loading the module
Specify the prebuilt module path
.. option:: -fmodule-header
Build a C++20 header unit from a header specified.
.. option:: -fmodule-header=\[user,system\]
Build a C++20 header unit, but search for the header in the user or system header search paths respectively.
.. option:: --hip-path=<arg>
HIP runtime installation path, used for finding HIP version and adding HIP include path.

View File

@ -56,6 +56,16 @@ enum LTOKind {
LTOK_Unknown
};
/// Whether headers used to construct C++20 module units should be looked
/// up by the path supplied on the command line, or in the user or system
/// search paths.
enum ModuleHeaderMode {
HeaderMode_None,
HeaderMode_Default,
HeaderMode_User,
HeaderMode_System
};
/// Driver - Encapsulate logic for constructing compilation processes
/// from a set of gcc-driver-like command line arguments.
class Driver {
@ -84,6 +94,13 @@ class Driver {
EmbedBitcode
} BitcodeEmbed;
/// Header unit mode set by -fmodule-header={user,system}.
ModuleHeaderMode CXX20HeaderType;
/// Set if we should process inputs and jobs with C++20 module
/// interpretation.
bool ModulesModeCXX20;
/// LTO mode selected via -f(no-)?lto(=.*)? options.
LTOKind LTOMode;
@ -574,6 +591,12 @@ public:
/// ShouldEmitStaticLibrary - Should the linker emit a static library.
bool ShouldEmitStaticLibrary(const llvm::opt::ArgList &Args) const;
/// Returns true if the user has indicated a C++20 header unit mode.
bool hasHeaderMode() const { return CXX20HeaderType != HeaderMode_None; }
/// Get the mode for handling headers as set by fmodule-header{=}.
ModuleHeaderMode getModuleHeaderMode() const { return CXX20HeaderType; }
/// Returns true if we are performing any kind of LTO.
bool isUsingLTO(bool IsOffload = false) const {
return getLTOMode(IsOffload) != LTOK_None;

View File

@ -2296,6 +2296,11 @@ defm implicit_modules : BoolFOption<"implicit-modules",
NegFlag<SetFalse, [CC1Option]>, PosFlag<SetTrue>, BothFlags<[NoXarchOption,CoreOption]>>;
def fretain_comments_from_system_headers : Flag<["-"], "fretain-comments-from-system-headers">, Group<f_Group>, Flags<[CC1Option]>,
MarshallingInfoFlag<LangOpts<"RetainCommentsFromSystemHeaders">>;
def fmodule_header : Flag <["-"], "fmodule-header">, Group<f_Group>,
Flags<[NoXarchOption]>, HelpText<"Build a C++20 Header Unit from a header.">;
def fmodule_header_EQ : Joined<["-"], "fmodule-header=">, Group<f_Group>,
Flags<[NoXarchOption]>, MetaVarName<"<kind>">,
HelpText<"Build a C++20 Header Unit from a header that should be found in the user (fmodule-header=user) or system (fmodule-header=system) search path.">;
def fno_knr_functions : Flag<["-"], "fno-knr-functions">, Group<f_Group>,
MarshallingInfoFlag<LangOpts<"DisableKNRFunctions">>,

View File

@ -191,13 +191,14 @@ Driver::Driver(StringRef ClangExecutable, StringRef TargetTriple,
DiagnosticsEngine &Diags, std::string Title,
IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS)
: Diags(Diags), VFS(std::move(VFS)), Mode(GCCMode),
SaveTemps(SaveTempsNone), BitcodeEmbed(EmbedNone), LTOMode(LTOK_None),
ClangExecutable(ClangExecutable), SysRoot(DEFAULT_SYSROOT),
DriverTitle(Title), CCCPrintBindings(false), CCPrintOptions(false),
CCPrintHeaders(false), CCLogDiagnostics(false), CCGenDiagnostics(false),
CCPrintProcessStats(false), TargetTriple(TargetTriple), Saver(Alloc),
CheckInputsExist(true), GenReproducer(false),
SuppressMissingInputWarning(false) {
SaveTemps(SaveTempsNone), BitcodeEmbed(EmbedNone),
CXX20HeaderType(HeaderMode_None), ModulesModeCXX20(false),
LTOMode(LTOK_None), ClangExecutable(ClangExecutable),
SysRoot(DEFAULT_SYSROOT), DriverTitle(Title), CCCPrintBindings(false),
CCPrintOptions(false), CCPrintHeaders(false), CCLogDiagnostics(false),
CCGenDiagnostics(false), CCPrintProcessStats(false),
TargetTriple(TargetTriple), Saver(Alloc), CheckInputsExist(true),
GenReproducer(false), SuppressMissingInputWarning(false) {
// Provide a sane fallback if no VFS is specified.
if (!this->VFS)
this->VFS = llvm::vfs::getRealFileSystem();
@ -337,9 +338,13 @@ phases::ID Driver::getFinalPhase(const DerivedArgList &DAL,
CCGenDiagnostics) {
FinalPhase = phases::Preprocess;
// --precompile only runs up to precompilation.
// --precompile only runs up to precompilation.
// Options that cause the output of C++20 compiled module interfaces or
// header units have the same effect.
} else if ((PhaseArg = DAL.getLastArg(options::OPT__precompile)) ||
(PhaseArg = DAL.getLastArg(options::OPT_extract_api))) {
(PhaseArg = DAL.getLastArg(options::OPT_extract_api)) ||
(PhaseArg = DAL.getLastArg(options::OPT_fmodule_header,
options::OPT_fmodule_header_EQ))) {
FinalPhase = phases::Precompile;
// -{fsyntax-only,-analyze,emit-ast} only run up to the compiler.
} else if ((PhaseArg = DAL.getLastArg(options::OPT_fsyntax_only)) ||
@ -1251,6 +1256,37 @@ Compilation *Driver::BuildCompilation(ArrayRef<const char *> ArgList) {
BitcodeEmbed = static_cast<BitcodeEmbedMode>(Model);
}
// Setting up the jobs for some precompile cases depends on whether we are
// treating them as PCH, implicit modules or C++20 ones.
// TODO: inferring the mode like this seems fragile (it meets the objective
// of not requiring anything new for operation, however).
const Arg *Std = Args.getLastArg(options::OPT_std_EQ);
ModulesModeCXX20 =
!Args.hasArg(options::OPT_fmodules) && Std &&
(Std->containsValue("c++20") || Std->containsValue("c++2b") ||
Std->containsValue("c++2a") || Std->containsValue("c++latest"));
// Process -fmodule-header{=} flags.
if (Arg *A = Args.getLastArg(options::OPT_fmodule_header_EQ,
options::OPT_fmodule_header)) {
// These flags force C++20 handling of headers.
ModulesModeCXX20 = true;
if (A->getOption().matches(options::OPT_fmodule_header))
CXX20HeaderType = HeaderMode_Default;
else {
StringRef ArgName = A->getValue();
unsigned Kind = llvm::StringSwitch<unsigned>(ArgName)
.Case("user", HeaderMode_User)
.Case("system", HeaderMode_System)
.Default(~0U);
if (Kind == ~0U) {
Diags.Report(diag::err_drv_invalid_value)
<< A->getAsString(Args) << ArgName;
} else
CXX20HeaderType = static_cast<ModuleHeaderMode>(Kind);
}
}
std::unique_ptr<llvm::opt::InputArgList> UArgs =
std::make_unique<InputArgList>(std::move(Args));
@ -2220,8 +2256,11 @@ bool Driver::DiagnoseInputExistence(const DerivedArgList &Args, StringRef Value,
return true;
// If it's a header to be found in the system or user search path, then defer
// complaints about its absence until those searches can be done.
if (Ty == types::TY_CXXSHeader || Ty == types::TY_CXXUHeader)
// complaints about its absence until those searches can be done. When we
// are definitely processing headers for C++20 header units, extend this to
// allow the user to put "-fmodule-header -xc++-header vector" for example.
if (Ty == types::TY_CXXSHeader || Ty == types::TY_CXXUHeader ||
(ModulesModeCXX20 && Ty == types::TY_CXXHeader))
return true;
if (getVFS().exists(Value))
@ -2287,6 +2326,21 @@ bool Driver::DiagnoseInputExistence(const DerivedArgList &Args, StringRef Value,
return false;
}
// Get the C++20 Header Unit type corresponding to the input type.
static types::ID CXXHeaderUnitType(ModuleHeaderMode HM) {
switch (HM) {
case HeaderMode_User:
return types::TY_CXXUHeader;
case HeaderMode_System:
return types::TY_CXXSHeader;
case HeaderMode_Default:
break;
case HeaderMode_None:
llvm_unreachable("should not be called in this case");
}
return types::TY_CXXHUHeader;
}
// Construct a the list of inputs and their types.
void Driver::BuildInputs(const ToolChain &TC, DerivedArgList &Args,
InputList &Inputs) const {
@ -2406,6 +2460,11 @@ void Driver::BuildInputs(const ToolChain &TC, DerivedArgList &Args,
else if (Args.hasArg(options::OPT_ObjCXX))
Ty = types::TY_ObjCXX;
}
// Disambiguate headers that are meant to be header units from those
// intended to be PCH.
if (Ty == types::TY_CXXHeader && hasHeaderMode())
Ty = CXXHeaderUnitType(CXX20HeaderType);
} else {
assert(InputTypeArg && "InputType set w/o InputTypeArg");
if (!InputTypeArg->getOption().matches(options::OPT_x)) {
@ -2457,6 +2516,11 @@ void Driver::BuildInputs(const ToolChain &TC, DerivedArgList &Args,
Diag(clang::diag::err_drv_unknown_language) << A->getValue();
InputType = types::TY_Object;
}
// If the user has put -fmodule-header{,=} then we treat C++ headers as
// header unit inputs. So we 'promote' -xc++-header appropriately.
if (InputType == types::TY_CXXHeader && hasHeaderMode())
InputType = CXXHeaderUnitType(CXX20HeaderType);
} else if (A->getOption().getID() == options::OPT_U) {
assert(A->getNumValues() == 1 && "The /U option has one value.");
StringRef Val = A->getValue(0);

View File

@ -0,0 +1,32 @@
// Test user-facing command line options to generate C++20 header units.
// RUN: %clang -### -std=c++20 -fmodule-header=user foo.hh 2>&1 | \
// RUN: FileCheck -check-prefix=CHECK-USER %s
// RUN: %clang -### -std=c++20 -fmodule-header=system foo.hh 2>&1 | \
// RUN: FileCheck -check-prefix=CHECK-SYS1 %s
// RUN: %clang -### -std=c++20 -fmodule-header=system \
// RUN: -xc++-system-header vector 2>&1 | FileCheck -check-prefix=CHECK-SYS2 %s
// RUN: %clang -### -std=c++20 -fmodule-header=system \
// RUN: -xc++-header vector 2>&1 | FileCheck -check-prefix=CHECK-SYS2 %s
// RUN: %clang -### -std=c++20 -fmodule-header %/S/Inputs/header-unit-01.hh \
// RUN: 2>&1 | FileCheck -check-prefix=CHECK-ABS %s -DTDIR=%/S/Inputs
// CHECK-USER: "-emit-header-unit"
// CHECK-USER-SAME: "-o" "foo.pcm"
// CHECK-USER-SAME: "-x" "c++-user-header" "foo.hh"
// CHECK-SYS1: "-emit-header-unit"
// CHECK-SYS1-SAME: "-o" "foo.pcm"
// CHECK-SYS1-SAME: "-x" "c++-system-header" "foo.hh"
// CHECK-SYS2: "-emit-header-unit"
// CHECK-SYS2-SAME: "-o" "vector.pcm"
// CHECK-SYS2-SAME: "-x" "c++-system-header" "vector"
// CHECK-ABS: "-emit-header-unit"
// CHECK-ABS-SAME: "-o" "header-unit-01.pcm"
// CHECK-ABS-SAME: "-x" "c++-header-unit-header" "[[TDIR]]/header-unit-01.hh"