Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[flang] MSVC runtime library selection #68017

Closed
bradking opened this issue Oct 2, 2023 · 21 comments · Fixed by #72519
Closed

[flang] MSVC runtime library selection #68017

bradking opened this issue Oct 2, 2023 · 21 comments · Fixed by #72519

Comments

@bradking
Copy link
Contributor

bradking commented Oct 2, 2023

For targets such as x86_64-pc-windows-msvc, LLVM/Flang generates
object files for the MSVC ABI and uses MSVC-compatible tooling.

Compilers that target the MSVC ABI need to select one of the MSVC
runtime libraries. They should define preprocessor macros identifying
the selected runtime library, and add #pragma comment-style annotations
to object files to pass /defaultlib:... flags to the linker without
build system intervention. With LLVM/Flang the latter is needed even
for a trivial empty Fortran program:

>type foo.f90
program foo
end program
>flang-new foo.f90
LINK : error LNK2001: unresolved external symbol mainCRTStartup
>flang-new foo.f90 -Xlinker -defaultlib:msvcrt
(works)

MSVC toolsets provide the following runtime library variants:

  • libcmt: Multi-threaded, optimized, linked statically.
    Defines -D_MT, links /defaultlib:libcmt.
    MSVC flag: cl -MT.

  • msvcrt: Multi-threaded, optimized, linked shared.
    Defines -D_MT -D_DLL, links /defaultlib:msvcrt.
    MSVC flag: cl -MD.

  • libcmtd: Multi-threaded, debug, linked statically.
    Defines -D_MT -D_DEBUG, links /defaultlib:libcmtd.
    MSVC flag: cl -MTd.

  • msvcrtd: Multi-threaded, debug, linked shared.
    Defines -D_MT -D_DEBUG -D_DLL, links /defaultlib:msvcrtd.
    MSVC flag: cl -MDd.

LLVM/Flang should be updated as follows:

  • Add command-line flags to select a MSVC runtime library variant.
  • Provide predefined macros indicating the selected MSVC runtime library
    for the preprocessor.
  • Generate /defaultlib:... flags in object files referencing the
    selected MSVC runtime library for the linker.

For reference, LLVM/Clang does all of these when targeting the MSVC ABI.

Additionally, LLVM/Flang's own runtime libraries (Fortran*.lib)
could be provided as variants corresponding to each MSVC runtime
library variant. Currently the Fortran*.lib libraries included in
the LLVM/Flang distribution only use the msvcrt runtime library.
LLVM/Flang should then write /defaultlib:... references in object
files, to link its own Fortran*.lib runtime libraries, matching the
selected MSVC runtime library variant.

Cc: @DavidTruby

@github-actions github-actions bot added the flang Flang issues not falling into any other category label Oct 2, 2023
@bradking
Copy link
Contributor Author

bradking commented Oct 2, 2023

#67675 is related in that it asks for MSVC-like predefined macros (identifying the ABI and target architecture).

@bradking
Copy link
Contributor Author

bradking commented Oct 2, 2023

For reference, this came up in discussion of CMake Issue 24840, requesting support for targeting the MSVC ABI with flang-new.

@EugeneZelenko EugeneZelenko added flang:driver platform:windows and removed flang Flang issues not falling into any other category labels Oct 3, 2023
@llvmbot
Copy link
Collaborator

llvmbot commented Oct 3, 2023

@llvm/issue-subscribers-flang-driver

For targets such as `x86_64-pc-windows-msvc`, LLVM/Flang generates object files for the MSVC ABI and uses MSVC-compatible tooling.

Compilers that target the MSVC ABI need to select one of the MSVC
runtime libraries. They should define preprocessor macros identifying
the selected runtime library, and add #pragma comment-style annotations
to object files to pass /defaultlib:... flags to the linker without
build system intervention. With LLVM/Flang the latter is needed even
for a trivial empty Fortran program:

>type foo.f90
program foo
end program
>flang-new foo.f90
LINK : error LNK2001: unresolved external symbol mainCRTStartup
>flang-new foo.f90 -Xlinker -defaultlib:msvcrt
(works)

MSVC toolsets provide the following runtime library variants:

  • libcmt: Multi-threaded, optimized, linked statically.
    Defines -D_MT, links /defaultlib:libcmt.
    MSVC flag: cl -MT.

  • msvcrt: Multi-threaded, optimized, linked shared.
    Defines -D_MT -D_DLL, links /defaultlib:msvcrt.
    MSVC flag: cl -MD.

  • libcmtd: Multi-threaded, debug, linked statically.
    Defines -D_MT -D_DEBUG, links /defaultlib:libcmtd.
    MSVC flag: cl -MTd.

  • msvcrtd: Multi-threaded, debug, linked shared.
    Defines -D_MT -D_DEBUG -D_DLL, links /defaultlib:msvcrtd.
    MSVC flag: cl -MDd.

LLVM/Flang should be updated as follows:

  • Add command-line flags to select a MSVC runtime library variant.
  • Provide predefined macros indicating the selected MSVC runtime library
    for the preprocessor.
  • Generate /defaultlib:... flags in object files referencing the
    selected MSVC runtime library for the linker.

For reference, LLVM/Clang does all of these when targeting the MSVC ABI.

Additionally, LLVM/Flang's own runtime libraries (Fortran*.lib)
could be provided as variants corresponding to each MSVC runtime
library variant. Currently the Fortran*.lib libraries included in
the LLVM/Flang distribution only use the msvcrt runtime library.
LLVM/Flang should then write /defaultlib:... references in object
files, to link its own Fortran*.lib runtime libraries, matching the
selected MSVC runtime library variant.

Cc: @DavidTruby

@DavidTruby
Copy link
Member

There’s an ongoing refactor of the flang runtime library happening at the moment but once that has landed I will take a look at this

@DavidTruby
Copy link
Member

The flang runtime library build refactor is talking a while so I'm going to pick this up and try and fix it separately to that

@DavidTruby DavidTruby self-assigned this Oct 27, 2023
@Meinersbur
Copy link
Member

Additionally, LLVM/Flang's own runtime libraries (Fortran*.lib) could be provided as variants corresponding to each MSVC runtime library variant.

AFAIK none of the other runtime-libraries (compiler-rt, libcxx, libomp) currently have this. Should this be done as a coordinated effort?

@DavidTruby
Copy link
Member

I was just checking and agree with @Meinersbur; at the moment every runtime uses just the static version. I think that doesn't preclude the end user linking to the dynamic one for their own program though, since no C++ classes cross the ABI boundary in the runtime? So a first step to get flang on the same level as everything else should be just to:

  • Always build the runtime as MT static
  • Give a flag for the end user selecting the dynamic or static runtime for their own code

Probably in the long run we want to be able to have both options for all of the runtimes. That will need to be done as a co-ordinated effort with all of the other runtimes since we depend on those in flang too. This part might have to wait until the flang runtime refactor though, after which the flang runtime will be built in the same way as the rest of the runtimes as I understand it.

@DavidTruby
Copy link
Member

On third thought; we don't have any dependency on the CRT outside the Fortran runtime, so I think we should just always link Fortran programs against the static runtime (libcmt) for now as I think it makes no difference other than introducing a dll dependency that isn't actually used to the generated binaries. What do you two think? I will admit to not being that familiar with how all this works on Windows

@bradking
Copy link
Contributor Author

If the Fortran runtime library has dependencies on the CRT, then the specific .obj files in the Fortran runtime library that have such dependence should have a /defaultlib:libcmt reference. That should cause the CRT to be linked only when the Fortran program relies on a Fortran runtime symbol whose implementation needs the CRT.

If the Fortran compiler itself never generates references to the CRT, then the only reason to have flags controlling which CRT to use would be if corresponding variants of the Fortran runtime library were available. Such flags would then control which Fortran runtime variant to name in /defaultlib:Fortran... references generated in the .obj files. Until multiple CRT variants of the Fortran runtime library are available, no such flags are needed.

However, even without multiple CRT variants of the Fortran runtime library, flang-new should still do the following:

  • The preprocessor should define the MSVC macro(s) associated with libcmt since that is the CRT that will ultimately be linked.
  • The compiler should emit /defaultlib:Fortran... references in the .obj files for any of the Fortran runtime libraries on which they depend. With that, the flang-new driver doesn't actually have to pass the .lib files to the linker.
  • The Fortran runtime libraries' individual object files should have /defaultlib:libcmt references if they rely on CRT symbols.

@DavidTruby
Copy link
Member

Ok yes then my understanding was correct (at least on the third try!) and I'll implement it that way with the changes you suggest. I do have one question/comment about it though, which is; I think the reason we pass the runtimes to the linker directly rather than relying on defaultlib is so that we can look up where they are e.g. in the LLVM build directory or install directory relative to the linker. I suppose we can pass -L or whatever the link.exe equivalent is instead, if we use defaultlib for the flang runtimes as well? Would this have better behaviour than what we do currently (just passing them manually) in some way I don't understand?

@bradking
Copy link
Contributor Author

IIRC in my local experiments with flang-new, it does pass the Fortran runtime library search path and the .lib file names separately and asks link.exe to search. With /defaultlib:Fortran*.lib references in all .obj files, it would only have to pass the search path to the linker so it can find them.

@DavidTruby
Copy link
Member

Ok sure, I'm happy to do it that way. I've hit another snag though, which is that compiler-rt/builtins is built with /MD by default, unlike the rest of compiler-rt. I'm not sure if that's intended or a cmake accident so I need to check with someone that knows compiler-rt better than I do to see, since our runtimes also depend on compiler-rt/builtins and so atm would still drag in the msvcrt runtime.

@bradking
Copy link
Contributor Author

Okay, thanks. Please post back here once we know whether that is on purpose.

Note that if the compiler emits references to symbols provided by a "builtins" library, a /defaultlib:... for that builtins library should be included in the .obj file too.

@DavidTruby
Copy link
Member

Does it work transitively? I.e. if I have FortranRuntime.lib which has /defaultlib:clang_rt.builtins and test.obj with /defaultlib:FortranRuntime.lib I think I don't also need /defaultlib:clang_rt.builtins in test.obj itself?

@DavidTruby
Copy link
Member

Let me loop back round for a second as I think I'm still not 100% understood where the issue is coming from. Let's say that for now (because the other libraries we depend on do the same) we just use whatever CRT the user built with, so by default /MD but I guess if the end user passes -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded then /MT (which is what the compiler-rt folks say happens atm). This is the current behaviour.
What is the issue here as it stands? That we don't define the preprocessor macros? I notice the original issue says that we aren't embedding /defaultlib:msvcrt.lib in the current case but that doesn't seem to be the case at least in my build, where I have the following in the directives section of FortranRuntime.lib:

   /DEFAULTLIB:msvcrt.lib
   /DEFAULTLIB:oldnames.lib
   /FAILIFMISMATCH:_MSC_VER=1900
   /FAILIFMISMATCH:_ITERATOR_DEBUG_LEVEL=0
   /FAILIFMISMATCH:RuntimeLibrary=MD_DynamicRelease
   /DEFAULTLIB:msvcprt.lib

@DavidTruby
Copy link
Member

The response wrt compiler-rt/builtins is that it deliberately doesn't actually depend on anything from the CRT, and so should be being built with the flag to indicate as such.
If that's the case, I think we're back to what I originally thought which is that we should build the Fortran runtime 4 times, once for each CRT, and provide a flag to select between them (linking the correct CRT at the same time, and providing the macros).

@bradking
Copy link
Contributor Author

Does it work transitively? I.e. if I have FortranRuntime.lib which has /defaultlib:clang_rt.builtins and test.obj with /defaultlib:FortranRuntime.lib I think I don't also need /defaultlib:clang_rt.builtins in test.obj itself?

It works transitively, but not necessarily at the granularity of whole libraries. Inside static libraries, the /defaultlib:... marks are carried by individual object files. If test.obj has /defaultlib:FortranRuntime.lib and depends on a symbol from an object file in FortranRuntime.lib, then it will use whatever /defaultlib:... references are recorded in that particular object file. If that object file happens to have /defaultlib:clang_rt.builtins then the builtins will be linked, but if the object file doesn't use any builtins in its implementation, it might not have that. Therefore if test.obj references a symbol provided by the builtins library, test.obj should have its own /defaultlib:clang_rt.builtins. It can have more than one, so if test.obj also references a symbol provided by the Fortran runtime library, it should also have /defaultlib:FortranRuntime.lib.

In general, whenever the compiler emits a reference to a symbol it expects to be provided by the Fortran runtime, clang-rt/builtins, or CRT, the object file the compiler writes should have a corresponding /defaultlib:....

@bradking
Copy link
Contributor Author

What is the issue here as it stands?

At least MSVC CRT preprocessor definitions and emitting /defaultlib:... references into an object file for all runtime/builtins libraries whose symbols it needs.

I notice the original issue says that we aren't embedding /defaultlib:msvcrt.lib in the current case but that doesn't seem to be the case at least in my build, where I have the following in the directives section of FortranRuntime.lib:

FortranRuntime.lib indeed has /defaultlib:msvcrt.lib, but the concern is about emitting /defaultlib:msvcrt.lib to object files when they need CRT symbols.

In the foo.f90 example in the description, the compiler emits an object file containing the program entry point. The linker complains it cannot find mainCRTStartup, a symbol from the CRT. When flang-new emits a program entry point, it should add /defaultlib:msvcrt.lib to get that. Currently it works without that in larger programs only by accident because they reference symbols in FortranRuntime.lib that happen to be in objects with /defaultlib:msvcrt.lib.

That is actually one case where we do need to directly reference the CRT from a Fortran object file. It should select the same CRT as FortranRuntime.lib will use. That means coordinating the /defaultlib:... CRT generation logic with the deployment of the compiler and its runtime libraries. They can either all hard-code msvcrt.lib, or all variants must be provided and the compiler needs a flag to select one.

@DavidTruby
Copy link
Member

Ok I see! I'm surprised it doesn't get the msvcrt.lib from Fortran_main.lib here which is where mainCRTStartup is referenced I believe. I suppose nothing from the Fortran side technically references into Fortran_main though so I guess it gets skipped because of this? We can probably just add a reference to the crt to any object file containing the program statement in Fortran.

@bradking
Copy link
Contributor Author

We can probably just add a reference to the crt to any object file containing the program statement in Fortran.

For that part of the issue, yes. Or, make sure that the program statement causes a symbol in Fortran_main to be referenced so that the CRT is linked through it. What does Fortran_main actually provide if an empty program can work without it?

@bradking
Copy link
Contributor Author

Hmm. Actually I see Fortran_main does end up getting used by the empty program. Fortran_main 's Fortran_main.c.obj provides main and depends on _FortranAProgramStart, which is provided by FortranRuntime's main.cpp.obj. Both of those object files already have /DEFAULTLIB:"MSVCRT" /DEFAULTLIB:"OLDNAMES". It is not clear why a separate /defaultlib:msvcrt flag is still needed to get mainCRTStartup when no other objects from FortranRuntime.lib are used.

DavidTruby added a commit that referenced this issue Nov 23, 2023
This patch uses the added --dependent-lib support to add the relevant
runtimes on MSVC targets as `/DEFAULTLIB:` sections in the object file
rather than on the link line. This should help CMake support for flang
on Windows.

Fixes #63741 
Fixes #68017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants