This is an archive of the discontinued LLVM Phabricator instance.

[Driver] [Modules] Support -fmodule-output (1/2)
ClosedPublic

Authored by ChuanqiXu on Oct 31 2022, 12:52 AM.

Details

Summary

Patches to support the one-phase compilation model for modules. This is the successor of D134267.

The behavior:
(1) If -o and -c is specified , the module file is in the same path within the same directory as the output the -o specified and with a new suffix .pcm.
(2) Otherwise, the module file is in the same path within the working directory directory with the name of the input file with a new suffix .pcm

For example,

Hello.cppm Use.cpp

A trivial one and the contents are ignored. When we run:

clang++ -std=c++20 -fmodule-output Hello.cppm -c

The directory would look like:

Hello.cppm  Hello.o  Hello.pcm Use.cpp

And if we run:

clang++ -std=c++20 -fmodule-output Hello.cppm -c -o output/Hello.o

Then the output directory may look like:

Hello.o  Hello.pcm

I'll add the ReleaseNotes and documentation after the series got accepted.

Diff Detail

Event Timeline

ChuanqiXu created this revision.Oct 31 2022, 12:52 AM
Herald added a project: Restricted Project. · View Herald TranscriptOct 31 2022, 12:52 AM
ChuanqiXu requested review of this revision.Oct 31 2022, 12:52 AM
Herald added a project: Restricted Project. · View Herald TranscriptOct 31 2022, 12:52 AM
ChuanqiXu edited the summary of this revision. (Show Details)
ChuanqiXu edited the summary of this revision. (Show Details)

@ben.boeckel maybe this'd be the place to discuss the motivation for this feature (picking up from your comment here: https://reviews.llvm.org/D134267#3892629 )

Could you expound a bit on why "write the .pcm to the same path as the .o" isn't sufficient? (like Split DWARF does this, for instance - not sure we have lots of other things to extrapolate from (admittedly there is a flag for specifying the filename for Split DWARF too, but it's not a terribly official thing (doesn't have a '-f', '-g', etc prefix))

@ben.boeckel maybe this'd be the place to discuss the motivation for this feature (picking up from your comment here: https://reviews.llvm.org/D134267#3892629 )

admittedly there is a flag for specifying the filename for Split DWARF too, but it's not a terribly official thing (doesn't have a '-f', '-g', etc prefix)

Although I agreed it would be better to wait for @ben.boeckel to answer the question, I still want to say "why we would offer something only if it is terribly bad". It looks like if it is normally bad, then we won't handle it and you need to workaround for it. I feel bad for the strategy.
Why can't we give some small help to make their lives easier.

Could you expound a bit on why "write the .pcm to the same path as the .o" isn't sufficient? (like Split DWARF does this, for instance - not sure we have lots of other things to extrapolate from (admittedly there is a flag for specifying the filename for Split DWARF too, but it's not a terribly official thing (doesn't have a '-f', '-g', etc prefix))

CMake doesn't support split dwarf (officially; it works, but CMake is just ignorant of it), so adapting to existing behavior would be more important there. For modules, however, explicit control is just much nicer to provide. It means that if heuristics change, CMake doesn't need patches to adapt to the logic. I would also vastly prefer that the .pcm filename match the *module* name, not the source file name. I'll also note that the example here is not suitable in the case of Hello.cxx and Hello.cpp existing at the same time (this is why CMake uses Hello.cxx.o). So some more control over the .pcm filename through more than source basename + .pcm is required in the general case (it is mostly mitigated if it is "replace .o with .pcm", but this is just unnecessary logic to ask anything interacting with Clang to deal with.

It also helps analyzers know that something else is happening explicitly and distributed builds to know what to bring back. Plus the other compilers offer controls over it; why does Clang have to be different?

dblaikie added a subscriber: rsmith.Nov 3 2022, 3:29 PM

I realize I got this jumbled up and the thread about "why do we need to name things" is meant to be over in D137059 (sorry @ben.boeckel :/ I know this is all confusing to follow at the best of times) so I'll pick that up there.

But maybe a relevant question /here/ (maybe @iains has some context here, and @ben.boeckel ): What are GCC's (& any other implementations) command line interfaces for things like this? How're the command line flags spelled? To see about inspiration for this.

(judging from the discourse thread with @rsmith it seems like "the ability to generate a .o from a .pcm" is disfavored, he didn't outright say "we should remove that functionality" but pretty close to it - in favor of ".cppm -> {.o, .pcm}" and ".ccpm -> .o" + ".cppm -> .pcm" (without the ability to generate the .o from the .pcm) - in which case we could change the existing driver behavior sooner or later to address that third action (.cppm -> .pcm but it's only a minimal pcm). Maybe we should have two flags one that says "produce a .pcm" (which we already have --precompile for that) and another that says "produce a .o" (I guess that's the default, so maybe we want a way to opt /out/ of that behavior?))

Eh, sorry, just talking myself around in circles.
Currently we have:
.cppm -> .o (no extra flags)
.cppm -> .pcm (--precompile)
.cppm -> .pcm + .o (unsupported)

I'm not sure that --precompile is the best flag name to be inspired by (it's unqualified by any -f or other prefix, which usually feels a bit weird, and it's a very generic term, doesn't mention modules, etc) - so I can appreciate the -fsave-std-c++-module-file name here, but it does sound a bit verbose? Wouldn't mind hearing other people's thoughts on flag names, especially any prior art/other implementation choices we could look for for inspiration.

iains added a comment.EditedNov 3 2022, 4:32 PM

I realize I got this jumbled up and the thread about "why do we need to name things" is meant to be over in D137059 (sorry @ben.boeckel :/ I know this is all confusing to follow at the best of times) so I'll pick that up there.

But maybe a relevant question /here/ (maybe @iains has some context here, and @ben.boeckel ): What are GCC's (& any other implementations) command line interfaces for things like this? How're the command line flags spelled? To see about inspiration for this.

.. the state I have is ...
As you note there are two parts to this:

  1. the ability to emit a BMI and object from one invocation of the compiler (which can be done as in this series by the driver - or with alternate patches in the FE - which allows for the improvements we want in the BMI)
  2. How to name the BMI.
      • in my experimentation with GCC, for this part, it defers the decision until the module name is known, and then queries an interface to ask for the BMI filename for that named module. The interface can be internal (e.g. choosing a simple default like source.pcm) or it could get the mapping from some other place, like a build system. The FE side of the transaction does not need to know how the name was chosen (so it _could_ be specified from a command-line flag).
    • but, AFAIR, GCC does not currently have such a flag.

(judging from the discourse thread with @rsmith it seems like "the ability to generate a .o from a .pcm" is disfavored, he didn't outright say "we should remove that functionality" but pretty close to it - in favor of ".cppm -> {.o, .pcm}" and ".ccpm -> .o" + ".cppm -> .pcm" (without the ability to generate the .o from the .pcm) - in which case we could change the existing driver behavior sooner or later to address that third action (.cppm -> .pcm but it's only a minimal pcm).

This is the strategy my draft patches for the FE aims for (apologies, for repeatedly mentioning a patch set that is not yet up for review ...)

Maybe we should have two flags one that says "produce a .pcm" (which we already have --precompile for that) and another that says "produce a .o" (I guess that's the default, so maybe we want a way to opt /out/ of that behavior?))

Eh, sorry, just talking myself around in circles.
Currently we have:
.cppm -> .o (no extra flags)
.cppm -> .pcm (--precompile)
.cppm -> .pcm + .o (unsupported)

I'm not sure that --precompile is the best flag name to be inspired by (it's unqualified by any -f or other prefix, which usually feels a bit weird, and it's a very generic term, doesn't mention modules, etc) -

GCC has "-fmodule-only" for which I have a patch (also unpublished) that aliases that to --precompile in the driver.
(I think we could possibly see a reason to have -fobject-only for symmetry and to cover the cases you mention here)

so I can appreciate the -fsave-std-c++-module-file name here, but it does sound a bit verbose? Wouldn't mind hearing other people's thoughts on flag names, especially any prior art/other implementation choices we could look for for inspiration.

What was the objection to "-fc++-module-filename[=]" ?

As noted above, if my memory is not too faulty, GCC does not have this specific [name the BMI] flag yet (I think this is the case because GCC essentially uses a simple in-process P1184 interface as the fallback when there's no external build system), then this flag name is up for grabs - and what is chosen here could, presumably, be implemented for GCC too.

Actually, in some ways clang does something pretty similar in that there's the in-process module-mapper - which is where the filenames probably should be decided, we just tend to bypass it with specific command line input for output filenames (which does not work where there are two names needed, except by providing some simplified fallback - like the "just change the extension" approach).

edited: to fix some bad formatting of the replies.

What was the objection to "-fc++-module-filename[=]" ?

I guess it reads a bit awkwardly when you aren't providing the filename/want the default filename?

GCC has "-fmodule-only"

Hmm, I don't mind that too much (& as you say, '-fobject-only' - though that flag is maybe too vague?) - but it does mean we'd need a separate flag to name the .pcm output file, because that flag ('-fmodule-only') wouldn't be present in all cases where the pcm is generated, only when it's pcm-but-no-object. Maybe less "exclusionary" flag names and more explicit (like '-fbuild-the-binary-module' and '-fbuild-the-object'). I guess most C++ GCC options ( https://gcc.gnu.org/onlinedocs/gcc/Option-Summary.html ) don't include "C++" in them, but the modules ones mostly include "module".

We could get out in front, planning for the world in which BMIs are really only the interface (& whatever else we want to carry for optimizing /use/ of that interface, but not complete enough to be usable for generating the modules object) and use -fmodule-interface[=] (skipping the 'binary' part) and -fmodule-implementation or -fmodule-object (which could go either way - default on or off, but provide -fno-module-object to do the "generate PCM only")?

Only other minor thing might be singular V plural - some of Clang's flags are -fmodules and others are -fmodule - any sense of what's likely to work better? (be nice to unify/standardize on one or the other, I can see having variation there might make for frustrating usability trying to remember which is which)

so I can appreciate the -fsave-std-c++-module-file name here, but it does sound a bit verbose?

My thought is the option is mainly used by build systems so that the users wouldn't type it frequently. In this case, I feel it might not be bad to make it a little bit redundant. Someone's redundancy is another's readability : )

Hmm, I don't mind that too much (& as you say, '-fobject-only' - though that flag is maybe too vague?) - but it does mean we'd need a separate flag to name the .pcm output file, because that flag ('-fmodule-only') wouldn't be present in all cases where the pcm is generated, only when it's pcm-but-no-object. Maybe less "exclusionary" flag names and more explicit (like '-fbuild-the-binary-module' and '-fbuild-the-object'). I guess most C++ GCC options ( https://gcc.gnu.org/onlinedocs/gcc/Option-Summary.html ) don't include "C++" in them, but the modules ones mostly include "module".

If this paragraph only talks about the GCC?

We could get out in front, planning for the world in which BMIs are really only the interface (& whatever else we want to carry for optimizing /use/ of that interface, but not complete enough to be usable for generating the modules object) and use -fmodule-interface[=] (skipping the 'binary' part) and -fmodule-implementation or -fmodule-object (which could go either way - default on or off, but provide -fno-module-object to do the "generate PCM only")?

For clang, I would like to add c++ in the option name since users are really easy to get confused with the existing clang modules. I've met many such cases. And for the proposal it self, I feel it is complex and hard to understand.

GCC has "-fmodule-only" for which I have a patch (also unpublished) that aliases that to --precompile in the driver.

This idea sounds good to me. But I would like to suggest to change the name to -fc++-module-interface-only. Due to the same reason above. The name -fmodule-only is not clear especially in the context of clang now.

Only other minor thing might be singular V plural - some of Clang's flags are -fmodules and others are -fmodule - any sense of what's likely to work better? (be nice to unify/standardize on one or the other, I can see having variation there might make for frustrating usability trying to remember which is which)

Yeah, sometimes it is frustrating. But I think we can't unify the existing options. It'd cause many breaking changes. And for module and modules, I prefer module a little more in practice.

dblaikie added inline comments.Nov 15 2022, 9:34 AM
clang/lib/Driver/Driver.cpp
5627–5628

Do we do that for -o today? (eg: if you try to -o and specify the input file name, such that the output would overwrite the input, what happens? I'm not sure - but I guess it's OK to do whatever that is for this case too)

clang/test/Driver/save-std-c++-module-file.cpp
1–11 ↗(On Diff #471912)

Usually driver tests only invoke the driver and observe the output using -### to see what subcommands the driver would run. Could this test be phrased in that way? (testing whatever command line is expected to be invoked, rather than the actual behavior of the frontend)

I'm not too concerned with the spelling of the flag myself.

ChuanqiXu updated this revision to Diff 475663.Nov 15 2022, 7:09 PM

Address comments.

ChuanqiXu marked 2 inline comments as done.Nov 15 2022, 7:10 PM
ChuanqiXu added inline comments.
clang/lib/Driver/Driver.cpp
5627–5628

Do we do that for -o today? (eg: if you try to -o and specify the input file name, such that the output would overwrite the input, what happens? I'm not sure - but I guess it's OK to do whatever that is for this case too)

In this case, the input file will be overwrite and no warning shows. So it looks like we didn't take special treatment here. So I remove the FIXME.

clang/test/Driver/save-std-c++-module-file.cpp
1–11 ↗(On Diff #471912)

Good point! Done.

dblaikie accepted this revision.Nov 16 2022, 11:16 AM

Couple of minor fixes for the test, but otherwise seems fine.

clang/test/Driver/save-std-c++-module-file.cpp
1–4 ↗(On Diff #475663)

Is this needed? Maybe we don't need to split the file, if it's just the one file anyway?

6 ↗(On Diff #475663)

Is the prefix needed? I'd expect we'd usually regex match away the actual directory with {{.*}} in tests like this?

This revision is now accepted and ready to land.Nov 16 2022, 11:16 AM
ChuanqiXu updated this revision to Diff 481503.Dec 8 2022, 7:31 PM
ChuanqiXu marked 2 inline comments as done.
ChuanqiXu retitled this revision from [Driver] [Modules] Support -fsave-std-c++-module-file (1/2) to [Driver] [Modules] Support -fmodule-output (1/2).

Rename the option into -fmodule-output according to the discussion result from: https://gcc.gnu.org/pipermail/gcc/2022-December/240239.html

iains added a comment.Dec 9 2022, 12:42 AM

thanks all for the patience on this one - and for the collaborative discussion - I do think the outcome is going to be an easier to remember option name ;)

thanks all for the patience on this one - and for the collaborative discussion - I do think the outcome is going to be an easier to remember option name ;)

Yeah. Thanks for your suggestion too. Otherwise I guess we won't get back : )

dblaikie requested changes to this revision.Dec 9 2022, 10:04 AM

Please update the patch description/commit message to reflect the new naming.

clang/test/Driver/save-std-c++-module-file.cpp
8–9 ↗(On Diff #481503)

Not sure I understand the need for two tests here - they both specify an absolute path to a .o file & CHECK that the absolute path matches the .pcm output file path, so they don't seem to be testing distinct scenarios?

14–15 ↗(On Diff #481503)

Might be worth fleshing out the CHECKs a bit more -

If this patch implements:

Support .cppm -> .pcm + .o in one phase.

Then I'd expect the test to demonstrate that - show that there are two commands run by the driver and one outputs the .o file (so include checking for the .o file output file name, and whatever flags tell the frontend that object code is to be emitted) and one that outputs the .pcm file (including checking the .pcm file output file name, and whatever flags tell the frontend that a pcm should be emitted).

1–4 ↗(On Diff #475663)

Ping on this ^

This revision now requires changes to proceed.Dec 9 2022, 10:04 AM
h-vetinari added inline comments.
clang/test/Driver/save-std-c++-module-file.cpp
1 ↗(On Diff #481503)

The filename of this test still reflects the old option name and should presumably be changed.

chfast added a subscriber: chfast.Dec 11 2022, 5:55 AM
ChuanqiXu updated this revision to Diff 481975.Dec 11 2022, 7:27 PM
ChuanqiXu marked 3 inline comments as done.
ChuanqiXu edited the summary of this revision. (Show Details)

Address comments.

ChuanqiXu marked an inline comment as done.Dec 11 2022, 7:28 PM
ChuanqiXu added inline comments.Dec 11 2022, 7:28 PM
clang/test/Driver/save-std-c++-module-file.cpp
8–9 ↗(On Diff #481503)

The above one doesn't specify the path for the .o file. So in the first test the .pcm file will be in the same directory with the input source file. And in the second command line, the .pcm file will be in the same directory with the .o file.

1–4 ↗(On Diff #475663)

Sorry for missing it. Thanks for clarification.

6 ↗(On Diff #475663)

Yeah, generally we would use {{.*}}. And -DPREFIX is more precise for what we want to test. So I feel it wouldn't be bad.

(I'd probably still reduce the test down to one example, using -o and skipping the extra OUTPUT_PATH details, only checking the last part of the path is as specified (or perhaps checking that it matches the .o file?))

Also, could you consider the questions @urnathan asked in the cross-project/flag naming thread about the semantics of this flag - I imagine the right behavior is "whatever we do for .o files", but it'd be good to check/consider/test them (they may not need automated testing - if the behaviour is implemented with the same utilities for .o files as for .pcm files then I don't mind just a statement that that's the case and some manual testing has been done to verify that all works)?

clang/test/Driver/save-std-c++-module-file.cpp
8–9 ↗(On Diff #481503)

Ah, maybe just testing the explicit -o version is adequate? We aren't doing anything interesting here except "whatever the .o path is" - so testing just "-o" seems sufficient to test the one path through this code. The pcm code doesn't have a "-o" and "implicit -o" codepath, just "do whatever the .o path is" so one test seems fine to me?

6 ↗(On Diff #475663)

I think the extra precision probably isn't necessary/sufficiently valuable & we could use {{.*}} here?

tahonermann added inline comments.Dec 12 2022, 2:39 PM
clang/lib/Driver/Driver.cpp
5627–5628

Basing the location of the module output on the presence of -o sounds confusing to me. Likewise, defaulting to writing next to the input file as opposed to the current directory (where a .o file is written by default) sounds wrong. I think this option should be handled similarly to -o; it should accept a path and:

  • If an absolute path is provided, write to that location.
  • If a relative path is provided, write to that location relative to the current working directory.

Leave it up to the user or build system to ensure that the .o and .pcm file locations coincide if that is what they want. In general, I don't expect colocation of .o and .pcm files to be what is desired.

dblaikie added inline comments.Dec 12 2022, 6:00 PM
clang/lib/Driver/Driver.cpp
5627–5628

@tahonermann there's precedent for this with Split DWARF (.dwo files go next to the .o file) & I'd argued for possibly only providing this behavior, letting consumers move files elsewhere if they needed to, but got voted down there.

I do think this is a reasonable default, though. Users and build systems have the option to pass a path to place the .pcm somewhere else (in the follow-up patch to this one).

5630–5634

It'd be nice if we didn't have to recompute this/lookup OPT_o here - any way we can use the object file output path here (that's already handled OPT_o or using the base input name, etc)?

ChuanqiXu marked an inline comment as done.
  • Reduce the test down to one example, using -o and skipping the extra OUTPUT_PATH details - as the comment required.

(I'd probably still reduce the test down to one example, using -o and skipping the extra OUTPUT_PATH details, only checking the last part of the path is as specified (or perhaps checking that it matches the .o file?))

Done.

Also, could you consider the questions @urnathan asked in the cross-project/flag naming thread about the semantics of this flag - I imagine the right behavior is "whatever we do for .o files", but it'd be good to check/consider/test them (they may not need automated testing - if the behaviour is implemented with the same utilities for .o files as for .pcm files then I don't mind just a statement that that's the case and some manual testing has been done to verify that all works)?

Yeah, I've replied in that thread. And it looks like we can't test the behaviors if we can only check the output of -### in this patch. I've made some manual testing locally and let's document the behaviors later.

clang/lib/Driver/Driver.cpp
5627–5628

@tahonermann here is another patch which implements the behavior you described: https://reviews.llvm.org/D137059

5630–5634

I didn't understand this a lot. We don't compute anything here and we just use the object file output path here if -o is provided and we replace the suffix then. I feel this is simple enough.

tahonermann added inline comments.Dec 13 2022, 8:00 AM
clang/lib/Driver/Driver.cpp
5627–5628

I'm ok with a default that consistently writes the PCM relative to the location of the .o file (if not overridden with an absolute path). What I'm not ok with is having the default be next to the .o file if -o is specified and next to the input file if -o is not specified. I don't think writing the PCM relative to the input file is a good default in any case. If -o is not specified, then I think it should be written relative to the current working directory; which matches where the .o file will be written.

tahonermann added inline comments.Dec 13 2022, 8:47 AM
clang/lib/Driver/Driver.cpp
5627–5628

Summary of the consensus of the Clang Modules WG:

  1. If -fmodule-output= is provided, the PCM is written to the indicated file (relative to the current working directory for a relative path).
  2. If -fmodule-output is provided, the PCM is written relative to the location of the .o file (the current working directory by default and the location indicated by -o otherwise).
ChuanqiXu edited the summary of this revision. (Show Details)

Address comments:

  • if -o is not provided, emit the module file to the working directory instead of the directory of the input file.
clang/lib/Driver/Driver.cpp
5627–5628

Done.

dblaikie added inline comments.Dec 14 2022, 12:49 PM
clang/lib/Driver/Driver.cpp
5630–5634

Computing the path to write to is what I'm referring to - and the fact that this had a bug (was relative to the source path instead of the CWD)/divergence from the -o path logic is the sort of thing I want to avoid.

I'm not sure there's an easy way to do this - but looks like the logic to name the .o file output is in Driver::GetNamedOutputPath and gets stored in the clang::driver::Compilation.... which I guess is where we are, but we're going through here with a PrecompileJobAction insntead of the compile job action, I suppose.

Could we keep these two codepaths closer together?

It'd be really nice if we could reuse that in some way.

Hmm, actually, why doesn't this fall out of the existing algorithm without modification?

Ah, I see, since the precompile action isn't "at top level" it gets a temporary file name - so if we change only that, it seems to fall out naturally:

diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp
index c7efe60b2335..db878cbfff46 100644
--- a/clang/lib/Driver/Driver.cpp
+++ b/clang/lib/Driver/Driver.cpp
@@ -5556,9 +5556,9 @@ const char *Driver::GetNamedOutputPath(Compilation &C, const JobAction &JA,
   }
 
   // Output to a temporary file?
-  if ((!AtTopLevel && !isSaveTempsEnabled() &&
+  if (((!AtTopLevel && !isSaveTempsEnabled() &&
        !C.getArgs().hasArg(options::OPT__SLASH_Fo)) ||
-      CCGenDiagnostics) {
+      CCGenDiagnostics) && JA.getType() != types::TY_ModuleFile) {
     StringRef Name = llvm::sys::path::filename(BaseInput);
     std::pair<StringRef, StringRef> Split = Name.split('.');
     const char *Suffix = types::getTypeTempSuffix(JA.getType(), IsCLMode());

Without the need to reimplement/duplicate the -o handling logic?

dblaikie added inline comments.Dec 14 2022, 12:52 PM
clang/lib/Driver/Driver.cpp
5630–5634

Oh, I should say, this patch didn't actually have the flag support, but it'd be something like this ^ but with the command line argument test as well (so "other stuff that's already there && !(TY_ModuleFile && hasArg fmodule-output)")

ChuanqiXu updated this revision to Diff 483074.Dec 14 2022, 7:49 PM

Address comments:

  • Reuse the logic to compute object file for module file when -o is not specified.
ChuanqiXu added inline comments.Dec 14 2022, 7:51 PM
clang/lib/Driver/Driver.cpp
5630–5634

To be honest, I prefer the previous patch. I feel it has higher readability. But this is a problem about taste and it doesn't have standard answer. Someone's readability is redundancy for others : )

dblaikie added inline comments.Dec 15 2022, 4:31 PM
clang/lib/Driver/Driver.cpp
5630–5634

I think there's real functionality we're at risk of missing by having separate implementations.

For instance - how does this interact with Apple's multiarch support (eg: clang++ test.cppm -fmodule-output -arch i386 -arch x86_64 -target x86_64_apple_darwin) - from my testing, without specifying an output file, you get something semi-usable/not simply incorrect: test-i386.pcm and test-x86_64.pcm. But if you try to name the output file you get foo.pcm and then another foo.pcm that overwrites the previous one. I think this could be taken care of if the suffix handling code was delegated down towards the else block that starts with SmallString<128> Output(getDefaultImageName());

But maybe solving that ^ problem could come out of a more general solution to the next problem:

What if you specify multiple source files on the command line without -c? Without -o you get test1.pcm and test2.pcm, but with -o foo you get foo.pcm overwriting foo.pcm. Perhaps if the output file specified isn't a .o file, we should ignore the -o and use the input-filename based naming? I guess we could reject this situation outright, and require the user to run multiple separate compilations. Though keeping features composable is nice.

Perhaps this needs a bit more consideration of some of these cases?

  • when -fmodule-output and -o are both specified, generate the module file into the directory of the output file and name the module file with the name of the input file with module file's suffixes. (Previously, the name of the module will be the same with the output file). The change tries to avoid rewrite the module file when we specify multiple input files in one command line.
ChuanqiXu added inline comments.Dec 16 2022, 12:30 AM
clang/lib/Driver/Driver.cpp
5630–5634

Oh, thanks for finding this. It is pretty bad that I didn't image we can specify multiple input module units in one command line.

What if you specify multiple source files on the command line without -c? Without -o you get test1.pcm and test2.pcm, but with -o foo you get foo.pcm overwriting foo.pcm. Perhaps if the output file specified isn't a .o file, we should ignore the -o and use the input-filename based naming? I guess we could reject this situation outright, and require the user to run multiple separate compilations. Though keeping features composable is nice.

Now the name of the module file will be the same with the input file no matter if we specified -o or not. With -o specified, the module files will be generated into the same directory with the output file. Without -o specified, the module files will be generated in the working directory. It'll still be problematic if the user specify two inputs with the same name in two different directory, the module file of the latter will overwrite the former one. But I guess we don't need to handle such cases?

For instance - how does this interact with Apple's multiarch support (eg: clang++ test.cppm -fmodule-output -arch i386 -arch x86_64 -target x86_64_apple_darwin) - from my testing, without specifying an output file, you get something semi-usable/not simply incorrect: test-i386.pcm and test-x86_64.pcm. But if you try to name the output file you get foo.pcm and then another foo.pcm that overwrites the previous one. I think this could be taken care of if the suffix handling code was delegated down towards the else block that starts with SmallString<128> Output(getDefaultImageName());

In the new implementation, I image we'll generate test-i386.pcm and test-x86_64.pcm even if we specified -o with -fmodule-output. But the weird thing is that when I tried to reproduce your example, the compiler told me the other archs are ignored. I'm not sure if I set something wrong or I must do it in Apple machine.

BTW, I am not sure if I misunderstand you, but the else block that starts with SmallString<128> Output(getDefaultImageName()); handles about IMAGE types, which looks irreverent to me.

tahonermann added inline comments.Dec 16 2022, 7:42 AM
clang/lib/Driver/Driver.cpp
5630–5634

how does this interact with Apple's multiarch support

Good question. What does split dwarf do in this case? Are differently named outputs generated or is a multi-arch dwarf file produced (assuming they exist)?

Rejecting the command line if it specifies multiple -arch options with -fmodule-output= seems fair to me (unless/until support for multi-arch .pcm files is added). How is this handled with -split_dwarf_output?

I'm not sure if I set something wrong or I must do it in Apple machine.

I don't recall for sure, but I think Apple Clang is needed. We noticed differences between community Clang and Apple Clang while I was at Coverity, but I don't recall the details.

ChuanqiXu added inline comments.Dec 18 2022, 10:17 PM
clang/lib/Driver/Driver.cpp
5630–5634

How is this handled with -split_dwarf_output?

Although I am not familiar with split-dwarf, according to https://github.com/llvm/llvm-project/blob/main/clang/lib/CodeGen/BackendUtil.cpp#L1076-L1087, it looks like -split_dwarf_output wouldn't handle the multiple -arch options. But I am not sure if the reason is the dwarf information is arch-independent (or not). Maybe we need to ask @dblaikie to make sure.

Rejecting the command line if it specifies multiple -arch options with -fmodule-output= seems fair to me (unless/until support for multi-arch .pcm files is added).

Yeah, it sounds good to me too. It is simpler and I feel like it wouldn't hurt so much if we don't support -fmodule-output with multiple arch.

(pulling this out from an inline comment, because it's hard to quote/smaller than it needs to be for more complex discussions):

Oh, thanks for finding this. It is pretty bad that I didn't image we can specify multiple input module units in one command line.

What if you specify multiple source files on the command line without -c? Without -o you get test1.pcm and test2.pcm, but with -o foo you get foo.pcm overwriting foo.pcm. Perhaps if the output file specified isn't a .o file, we should ignore the -o and use the input-filename based naming? I guess we could reject this situation outright, and require the user to run multiple separate compilations. Though keeping features composable is nice.

Now the name of the module file will be the same with the input file no matter if we specified -o or not. With -o specified, the module files will be generated into the same directory with the output file. Without -o specified, the module files will be generated in the working directory. It'll still be problematic if the user specify two inputs with the same name in two different directory, the module file of the latter will overwrite the former one. But I guess we don't need to handle such cases?

For instance - how does this interact with Apple's multiarch support (eg: clang++ test.cppm -fmodule-output -arch i386 -arch x86_64 -target x86_64_apple_darwin) - from my testing, without specifying an output file, you get something semi-usable/not simply incorrect: test-i386.pcm and test-x86_64.pcm. But if you try to name the output file you get foo.pcm and then another foo.pcm that overwrites the previous one. I think this could be taken care of if the suffix handling code was delegated down towards the else block that starts with SmallString<128> Output(getDefaultImageName());

In the new implementation, I image we'll generate test-i386.pcm and test-x86_64.pcm even if we specified -o with -fmodule-output. But the weird thing is that when I tried to reproduce your example, the compiler told me the other archs are ignored. I'm not sure if I set something wrong or I must do it in Apple machine.

BTW, I am not sure if I misunderstand you, but the else block that starts with SmallString<128> Output(getDefaultImageName()); handles about IMAGE types, which looks irreverent to me.

how does this interact with Apple's multiarch support

Good question. What does split dwarf do in this case? Are differently named outputs generated or is a multi-arch dwarf file produced (assuming they exist)?

Split DWARF isn't supported on Darwin/MacOS/whatever it's called - the MachO debug info distribution model looks more like, but isn't quite, Split DWARF already (it keeps the debug info in .o files, doesn't link them into the final executable - and you can make a separate debug info archive with dsymutil).

Rejecting the command line if it specifies multiple -arch options with -fmodule-output= seems fair to me (unless/until support for multi-arch .pcm files is added).

Yeah, trying to see if

I'm not sure if I set something wrong or I must do it in Apple machine.

I don't recall for sure, but I think Apple Clang is needed. We noticed differences between community Clang and Apple Clang while I was at Coverity, but I don't recall the details.

I don't think this needs Apple Clang, but it does need a Darwin triple. I was reproducing/examining the behavior with clang++ test.cppm -fmodule-output -arch i386 -arch x86_64 -### -target x86_64-apple-darwin.

ChuanqiXu updated this revision to Diff 485457.Dec 27 2022, 9:50 PM

Address comments: Reject the command line if it specifies -fmodule-output and multiple arch.

I don't know the driver code very well, but as best I can tell, this appears to match the design agreed to at the last Clang Modules WG meeting.

clang/test/Driver/module-output.cppm
6
19
27
ChuanqiXu updated this revision to Diff 486132.Jan 3 2023, 6:28 PM

Address comments.

@dblaikie would you like to take a look at this?

Without undue haste, I just wanted to comment from the peanut gallery that it would be amazing if the patches that are necessary for CMake + Clang to use C++ modules would make the cut-off for LLVM 16 that's coming up around the 24th of January.

Without undue haste, I just wanted to comment from the peanut gallery that it would be amazing if the patches that are necessary for CMake + Clang to use C++ modules would make the cut-off for LLVM 16 that's coming up around the 24th of January.

Thanks for the note. I'll land this patch in the next week if no further comments come in. BTW, the series of clang-scan-deps patch (https://reviews.llvm.org/D139168) is also necessary but I am not sure if we can land them in time. I guess it may be possible to backport these patches to 16.x since it is relatively important?

BTW, the series of clang-scan-deps patch (https://reviews.llvm.org/D139168) is also necessary [...]

Yes, I meant both those series.

[...] but I am not sure if we can land them in time. I guess it may be possible to backport these patches to 16.x since it is relatively important?

Might make sense tagging the release managers if things get tight - perhaps it's possible. E.g. ranges for LLVM 15 was also finished after the branch.

@dblaikie would you like to take a look at this?

Yep, this is still on my radar - sorry for the delays. I'll get to it.

dblaikie added inline comments.Jan 10 2023, 12:26 PM
clang/lib/Driver/Driver.cpp
5814–5816

These conditions might be shared with the previous condition that tests the opposite?

5817

Is there some way we can avoid this (-fmodule-output -o ...) being a special case? It'd be good if we could use a single common implementation regardless of whether -o is used (ie: Figure out the output file name (whether it's implicitly determined by clang, in the absence of -o, or explicitly provided by the user through -o) and then replace the suffix with pcm)?

ChuanqiXu updated this revision to Diff 488057.Jan 10 2023, 6:44 PM

Address comments: merge the conditions.

ChuanqiXu marked an inline comment as done.Jan 10 2023, 6:45 PM
ChuanqiXu added inline comments.
clang/lib/Driver/Driver.cpp
5817

I guess we can't do it or we can't do it easily. Otherwise the .pcm file will always lives in the same directory with the input file.

dblaikie added inline comments.Jan 11 2023, 1:33 PM
clang/lib/Driver/Driver.cpp
5817

I guess we can't do it or we can't do it easily. Otherwise the .pcm file will always lives in the same directory with the input file.

I don't follow/understand. What I mean is, I'd like it, if possible, this was implemented by something like "find the path for the .o file output, then replace the extension with .pcm".

I worry atht code like this that recomputes something similar to the object output path risks getting out of sync with the actual object path.

tahonermann added inline comments.Jan 11 2023, 1:52 PM
clang/lib/Driver/Driver.cpp
5817

That is certainly a valid concern and I agree it would be better if the shared output path is computed exactly once. If that would require significant change, then tests to ensure consistent behavior would be the next best thing. I'm not sure what infrastructure is in place for validation of output file locations though.

dblaikie added inline comments.Jan 11 2023, 3:58 PM
clang/lib/Driver/Driver.cpp
5817

Well, I guess the Split DWARF file naming isn't fundamentally better - it looks at the OPT_O argument directly too:
https://github.com/llvm/llvm-project/blob/main/clang/lib/Driver/ToolChains/CommonArgs.cpp#L1250

Perhaps we could at least reuse this same logic - make it a reusable function in some way? (for instance, it checks -c too, which seems relevant - we wouldn't want to name the .pcm after the linked executable name, right?

ChuanqiXu updated this revision to Diff 488455.Jan 11 2023, 7:12 PM

Address comments:

  • Extract the logic to compute the output path of -fmodule-output to a reusable function.
ChuanqiXu edited the summary of this revision. (Show Details)Jan 11 2023, 7:12 PM
ChuanqiXu marked an inline comment as done.
ChuanqiXu added inline comments.
clang/lib/Driver/Driver.cpp
5817

Perhaps we could at least reuse this same logic - make it a reusable function in some way?

Done. It looks better now.

(for instance, it checks -c too, which seems relevant - we wouldn't want to name the .pcm after the linked executable name, right?

Oh, right. Although the previous conclusion is that if -o is specified, the .pcm file should be in the same dir with the output. But it is indeed weird that the .pcm file are related to the linked executable if we thought it deeper.

dblaikie added inline comments.Jan 12 2023, 9:33 AM
clang/lib/Driver/Driver.cpp
5817

Ah, I was hoping it could reuse the same code, rather than duplicate it - any chance it could be refactored into a common implementation between Split DWARF and modules?

h-vetinari added inline comments.Jan 12 2023, 4:58 PM
clang/lib/Driver/Driver.cpp
5639–5640
5817

Ah, I was hoping it could reuse the same code, rather than duplicate it - any chance it could be refactored into a common implementation between Split DWARF and modules?

Could we uncouple this clean-up from landing the patches before the LLVM 16 branch? A trivial refactor might even still be backportable, but the whole series will be more challenging.

ChuanqiXu updated this revision to Diff 488836.Jan 12 2023, 6:06 PM
ChuanqiXu marked an inline comment as done.

Address comments: fix a typo.

ChuanqiXu marked an inline comment as done.Jan 12 2023, 6:08 PM
ChuanqiXu added inline comments.
clang/lib/Driver/Driver.cpp
5817

Ah, I was hoping it could reuse the same code, rather than duplicate it - any chance it could be refactored into a common implementation between Split DWARF and modules?

I feel it is an overkill to merge these 2 logics. It will become harder to read and modify the logics after we merge them. I feel better with the current implementation since it has fewer code dependencies and its complexity is low.

dblaikie accepted this revision.Jan 12 2023, 6:59 PM

I really don't think this is the right thing to do - the Split DWARF code, for instance, has support for GPU bundling that's missing in the module file naming code, which seems likely to be broken & within reason handle-able today by reusing the Split DWARF code, similarly with the multi-arch bundling for MachO. But I've tried to explain these things in several ways, and haven't managed to connect.

Carry on.

This revision is now accepted and ready to land.Jan 12 2023, 6:59 PM

I really don't think this is the right thing to do - the Split DWARF code, for instance, has support for GPU bundling that's missing in the module file naming code, which seems likely to be broken & within reason handle-able today by reusing the Split DWARF code, similarly with the multi-arch bundling for MachO. But I've tried to explain these things in several ways, and haven't managed to connect.

Carry on.

Thanks for your patient reviewing! We can merge the logics with Split DWARF code someday when we find it necessary.

This revision was landed with ongoing or failed builds.Jan 15 2023, 7:08 PM
This revision was automatically updated to reflect the committed changes.

I added // REQUIRES: x86-registered-target. Is it still failing?

I added // REQUIRES: x86-registered-target. Is it still failing?

It is still failing yes, I think it should restrict system-aix instead, like you did on windows.

I really don't think this is the right thing to do - the Split DWARF code, for instance, has support for GPU bundling that's missing in the module file naming code, which seems likely to be broken & within reason handle-able today by reusing the Split DWARF code, similarly with the multi-arch bundling for MachO. But I've tried to explain these things in several ways, and haven't managed to connect.

Carry on.

Thanks for your patient reviewing! We can merge the logics with Split DWARF code someday when we find it necessary.

My concern is that when it becomes necessary it won't be apparent - someone will fix (or introduce) a bug in one codepath, unaware of the other similar codepath. Unifying them before that happens is valuable.

I added // REQUIRES: x86-registered-target. Is it still failing?

It is still failing yes, I think it should restrict system-aix instead, like you did on windows.

Got it. Could you help to add this? Since I can't test it properly.

My concern is that when it becomes necessary it won't be apparent - someone will fix (or introduce) a bug in one codepath, unaware of the other similar codepath. Unifying them before that happens is valuable.

Understood. I just felt that the tools::SplitDebugName and GetModuleOutputPath have more different points than common points.