Skip to content

Commit 8d53866

Browse files
authored
[flang] Generate main only when a Fortran program statement is present (llvm#89938)
This patch changes the behaviour for flang to only create and link to a `main` entry point when the Fortran code has a program statement in it. This means that flang-new can be used to link even when the program is a mixed C/Fortran code with `main` present in C and no entry point present in Fortran. This also removes the `-fno-fortran-main` flag as this no longer has any functionality.
1 parent de6b2b9 commit 8d53866

File tree

22 files changed

+287
-226
lines changed

22 files changed

+287
-226
lines changed

clang/include/clang/Driver/Options.td

-6
Original file line numberDiff line numberDiff line change
@@ -6589,12 +6589,6 @@ def J : JoinedOrSeparate<["-"], "J">,
65896589
Group<gfortran_Group>,
65906590
Alias<module_dir>;
65916591

6592-
let Visibility = [FlangOption] in {
6593-
def no_fortran_main : Flag<["-"], "fno-fortran-main">,
6594-
Visibility<[FlangOption]>, Group<f_Group>,
6595-
HelpText<"Do not include Fortran_main.a (provided by Flang) when linking">;
6596-
} // let Visibility = [ FlangOption ]
6597-
65986592
//===----------------------------------------------------------------------===//
65996593
// FC1 Options
66006594
//===----------------------------------------------------------------------===//

clang/lib/Driver/ToolChains/CommonArgs.cpp

+1-109
Original file line numberDiff line numberDiff line change
@@ -1191,118 +1191,10 @@ bool tools::addOpenMPRuntime(const Compilation &C, ArgStringList &CmdArgs,
11911191
return true;
11921192
}
11931193

1194-
/// Determines if --whole-archive is active in the list of arguments.
1195-
static bool isWholeArchivePresent(const ArgList &Args) {
1196-
bool WholeArchiveActive = false;
1197-
for (auto *Arg : Args.filtered(options::OPT_Wl_COMMA)) {
1198-
if (Arg) {
1199-
for (StringRef ArgValue : Arg->getValues()) {
1200-
if (ArgValue == "--whole-archive")
1201-
WholeArchiveActive = true;
1202-
if (ArgValue == "--no-whole-archive")
1203-
WholeArchiveActive = false;
1204-
}
1205-
}
1206-
}
1207-
1208-
return WholeArchiveActive;
1209-
}
1210-
1211-
/// Determine if driver is invoked to create a shared object library (-static)
1212-
static bool isSharedLinkage(const ArgList &Args) {
1213-
return Args.hasArg(options::OPT_shared);
1214-
}
1215-
1216-
/// Determine if driver is invoked to create a static object library (-shared)
1217-
static bool isStaticLinkage(const ArgList &Args) {
1218-
return Args.hasArg(options::OPT_static);
1219-
}
1220-
1221-
/// Add Fortran runtime libs for MSVC
1222-
static void addFortranRuntimeLibsMSVC(const ArgList &Args,
1223-
llvm::opt::ArgStringList &CmdArgs) {
1224-
unsigned RTOptionID = options::OPT__SLASH_MT;
1225-
if (auto *rtl = Args.getLastArg(options::OPT_fms_runtime_lib_EQ)) {
1226-
RTOptionID = llvm::StringSwitch<unsigned>(rtl->getValue())
1227-
.Case("static", options::OPT__SLASH_MT)
1228-
.Case("static_dbg", options::OPT__SLASH_MTd)
1229-
.Case("dll", options::OPT__SLASH_MD)
1230-
.Case("dll_dbg", options::OPT__SLASH_MDd)
1231-
.Default(options::OPT__SLASH_MT);
1232-
}
1233-
switch (RTOptionID) {
1234-
case options::OPT__SLASH_MT:
1235-
CmdArgs.push_back("/WHOLEARCHIVE:Fortran_main.static.lib");
1236-
break;
1237-
case options::OPT__SLASH_MTd:
1238-
CmdArgs.push_back("/WHOLEARCHIVE:Fortran_main.static_dbg.lib");
1239-
break;
1240-
case options::OPT__SLASH_MD:
1241-
CmdArgs.push_back("/WHOLEARCHIVE:Fortran_main.dynamic.lib");
1242-
break;
1243-
case options::OPT__SLASH_MDd:
1244-
CmdArgs.push_back("/WHOLEARCHIVE:Fortran_main.dynamic_dbg.lib");
1245-
break;
1246-
}
1247-
}
1248-
1249-
// Add FortranMain runtime lib
1250-
static void addFortranMain(const ToolChain &TC, const ArgList &Args,
1251-
llvm::opt::ArgStringList &CmdArgs) {
1252-
// 0. Shared-library linkage
1253-
// If we are attempting to link a library, we should not add
1254-
// -lFortran_main.a to the link line, as the `main` symbol is not
1255-
// required for a library and should also be provided by one of
1256-
// the translation units of the code that this shared library
1257-
// will be linked against eventually.
1258-
if (isSharedLinkage(Args) || isStaticLinkage(Args)) {
1259-
return;
1260-
}
1261-
1262-
// 1. MSVC
1263-
if (TC.getTriple().isKnownWindowsMSVCEnvironment()) {
1264-
addFortranRuntimeLibsMSVC(Args, CmdArgs);
1265-
return;
1266-
}
1267-
1268-
// 2. GNU and similar
1269-
const Driver &D = TC.getDriver();
1270-
const char *FortranMainLinkFlag = "-lFortran_main";
1271-
1272-
// Warn if the user added `-lFortran_main` - this library is an implementation
1273-
// detail of Flang and should be handled automaticaly by the driver.
1274-
for (const char *arg : CmdArgs) {
1275-
if (strncmp(arg, FortranMainLinkFlag, strlen(FortranMainLinkFlag)) == 0)
1276-
D.Diag(diag::warn_drv_deprecated_custom)
1277-
<< FortranMainLinkFlag
1278-
<< "see the Flang driver documentation for correct usage";
1279-
}
1280-
1281-
// The --whole-archive option needs to be part of the link line to make
1282-
// sure that the main() function from Fortran_main.a is pulled in by the
1283-
// linker. However, it shouldn't be used if it's already active.
1284-
// TODO: Find an equivalent of `--whole-archive` for Darwin and AIX.
1285-
if (!isWholeArchivePresent(Args) && !TC.getTriple().isMacOSX() &&
1286-
!TC.getTriple().isOSAIX()) {
1287-
CmdArgs.push_back("--whole-archive");
1288-
CmdArgs.push_back(FortranMainLinkFlag);
1289-
CmdArgs.push_back("--no-whole-archive");
1290-
return;
1291-
}
1292-
1293-
CmdArgs.push_back(FortranMainLinkFlag);
1294-
}
1295-
12961194
/// Add Fortran runtime libs
12971195
void tools::addFortranRuntimeLibs(const ToolChain &TC, const ArgList &Args,
12981196
llvm::opt::ArgStringList &CmdArgs) {
1299-
// 1. Link FortranMain
1300-
// FortranMain depends on FortranRuntime, so needs to be listed first. If
1301-
// -fno-fortran-main has been passed, skip linking Fortran_main.a
1302-
if (!Args.hasArg(options::OPT_no_fortran_main))
1303-
addFortranMain(TC, Args, CmdArgs);
1304-
1305-
// 2. Link FortranRuntime and FortranDecimal
1197+
// Link FortranRuntime and FortranDecimal
13061198
// These are handled earlier on Windows by telling the frontend driver to
13071199
// add the correct libraries to link against as dependents in the object
13081200
// file.

clang/lib/Driver/ToolChains/Flang.cpp

-9
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,6 @@ static void processVSRuntimeLibrary(const ToolChain &TC, const ArgList &Args,
282282
assert(TC.getTriple().isKnownWindowsMSVCEnvironment() &&
283283
"can only add VS runtime library on Windows!");
284284
// if -fno-fortran-main has been passed, skip linking Fortran_main.a
285-
bool LinkFortranMain = !Args.hasArg(options::OPT_no_fortran_main);
286285
if (TC.getTriple().isKnownWindowsMSVCEnvironment()) {
287286
CmdArgs.push_back(Args.MakeArgString(
288287
"--dependent-lib=" + TC.getCompilerRTBasename(Args, "builtins")));
@@ -300,26 +299,20 @@ static void processVSRuntimeLibrary(const ToolChain &TC, const ArgList &Args,
300299
case options::OPT__SLASH_MT:
301300
CmdArgs.push_back("-D_MT");
302301
CmdArgs.push_back("--dependent-lib=libcmt");
303-
if (LinkFortranMain)
304-
CmdArgs.push_back("--dependent-lib=Fortran_main.static.lib");
305302
CmdArgs.push_back("--dependent-lib=FortranRuntime.static.lib");
306303
CmdArgs.push_back("--dependent-lib=FortranDecimal.static.lib");
307304
break;
308305
case options::OPT__SLASH_MTd:
309306
CmdArgs.push_back("-D_MT");
310307
CmdArgs.push_back("-D_DEBUG");
311308
CmdArgs.push_back("--dependent-lib=libcmtd");
312-
if (LinkFortranMain)
313-
CmdArgs.push_back("--dependent-lib=Fortran_main.static_dbg.lib");
314309
CmdArgs.push_back("--dependent-lib=FortranRuntime.static_dbg.lib");
315310
CmdArgs.push_back("--dependent-lib=FortranDecimal.static_dbg.lib");
316311
break;
317312
case options::OPT__SLASH_MD:
318313
CmdArgs.push_back("-D_MT");
319314
CmdArgs.push_back("-D_DLL");
320315
CmdArgs.push_back("--dependent-lib=msvcrt");
321-
if (LinkFortranMain)
322-
CmdArgs.push_back("--dependent-lib=Fortran_main.dynamic.lib");
323316
CmdArgs.push_back("--dependent-lib=FortranRuntime.dynamic.lib");
324317
CmdArgs.push_back("--dependent-lib=FortranDecimal.dynamic.lib");
325318
break;
@@ -328,8 +321,6 @@ static void processVSRuntimeLibrary(const ToolChain &TC, const ArgList &Args,
328321
CmdArgs.push_back("-D_DEBUG");
329322
CmdArgs.push_back("-D_DLL");
330323
CmdArgs.push_back("--dependent-lib=msvcrtd");
331-
if (LinkFortranMain)
332-
CmdArgs.push_back("--dependent-lib=Fortran_main.dynamic_dbg.lib");
333324
CmdArgs.push_back("--dependent-lib=FortranRuntime.dynamic_dbg.lib");
334325
CmdArgs.push_back("--dependent-lib=FortranDecimal.dynamic_dbg.lib");
335326
break;

flang/docs/FlangDriver.md

+3-29
Original file line numberDiff line numberDiff line change
@@ -179,46 +179,20 @@ like this:
179179

180180
```
181181
$ flang -v -o example example.o
182-
"/usr/bin/ld" [...] example.o [...] "--whole-archive" "-lFortran_main"
183-
"--no-whole-archive" "-lFortranRuntime" "-lFortranDecimal" [...]
182+
"/usr/bin/ld" [...] example.o [...] "-lFortranRuntime" "-lFortranDecimal" [...]
184183
```
185184

186185
The automatically added libraries are:
187186

188-
* `Fortran_main`: Provides the main entry point `main` that then invokes
189-
`_QQmain` with the Fortran program unit. This library has a dependency to
190-
the `FortranRuntime` library.
191187
* `FortranRuntime`: Provides most of the Flang runtime library.
192188
* `FortranDecimal`: Provides operations for decimal numbers.
193189

194-
The default is that, when using Flang as the linker, one of the Fortran
195-
translation units provides the program unit and therefore it is assumed that
196-
Fortran is the main code part (calling into C/C++ routines via `BIND (C)`
197-
interfaces). When composing the linker commandline, Flang uses
198-
`--whole-archive` and `--no-whole-archive` (Windows: `/WHOLEARCHIVE:`,
199-
Darwin & AIX: *not implemented yet*) to make sure that all for `Fortran_main`
200-
is processed by the linker. This is done to issue a proper error message when
201-
multiple definitions of `main` occur. This happens, for instance, when linking
202-
a code that has a Fortran program unit with a C/C++ code that also defines a
203-
`main` function. A user may be required to explicitly provide the C++ runtime
204-
libraries at link time (e.g., via `-lstdc++` for STL)
205-
206190
If the code is C/C++ based and invokes Fortran routines, one can either use Clang
207191
or Flang as the linker driver. If Clang is used, it will automatically all
208192
required runtime libraries needed by C++ (e.g., for STL) to the linker invocation.
209193
In this case, one has to explicitly provide the Fortran runtime libraries
210-
`FortranRuntime` and/or `FortranDecimal`. An alternative is to use Flang to link
211-
and use the `-fno-fortran-main` flag. This flag removes
212-
`Fortran_main` from the linker stage and hence requires one of the C/C++
213-
translation units to provide a definition of the `main` function. In this case,
214-
it may be required to explicitly supply C++ runtime libraries as mentioned above.
215-
216-
When creating shared or static libraries using Flang with `-shared` or `-static`
217-
flag, Fortran_main is automatically removed from the linker stage (i.e.,
218-
`-fno-fortran-main` is on by default). It is assumed that when creating a
219-
static or shared library, the generated library does not need a `main`
220-
function, as a final link stage will occur that will provide the `Fortran_main`
221-
library when creating the final executable.
194+
`FortranRuntime` and/or `FortranDecimal`. An alternative is to use Flang to link.
195+
In this case, it may be required to explicitly supply C++ runtime libraries.
222196

223197
On Darwin, the logical root where the system libraries are located (sysroot)
224198
must be specified. This can be done with the CMake build flag `DEFAULT_SYSROOT`

flang/include/flang/Optimizer/Builder/Runtime/EnvironmentDefaults.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
namespace fir {
2424
class FirOpBuilder;
25+
class GlobalOp;
2526
} // namespace fir
2627

2728
namespace mlir {
@@ -37,7 +38,7 @@ namespace fir::runtime {
3738
/// Create the list of environment variable defaults for the runtime to set. The
3839
/// form of the generated list is defined in the runtime header file
3940
/// environment-default-list.h
40-
void genEnvironmentDefaults(
41+
fir::GlobalOp genEnvironmentDefaults(
4142
fir::FirOpBuilder &builder, mlir::Location loc,
4243
const std::vector<Fortran::lower::EnvironmentDefault> &envDefaults);
4344

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//===-- Main.h - generate main runtime API calls ----------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef FORTRAN_OPTIMIZER_BUILDER_RUNTIME_MAIN_H
10+
#define FORTRAN_OPTIMIZER_BUILDER_RUNTIME_MAIN_H
11+
12+
namespace mlir {
13+
class Location;
14+
} // namespace mlir
15+
16+
namespace fir {
17+
class FirOpBuilder;
18+
class GlobalOp;
19+
} // namespace fir
20+
21+
namespace fir::runtime {
22+
23+
void genMain(fir::FirOpBuilder &builder, mlir::Location loc,
24+
fir::GlobalOp &env);
25+
26+
}
27+
28+
#endif // FORTRAN_OPTIMIZER_BUILDER_RUNTIME_MAIN_H

flang/lib/Lower/Bridge.cpp

+5-2
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
#include "flang/Optimizer/Builder/Runtime/Character.h"
3737
#include "flang/Optimizer/Builder/Runtime/Derived.h"
3838
#include "flang/Optimizer/Builder/Runtime/EnvironmentDefaults.h"
39+
#include "flang/Optimizer/Builder/Runtime/Main.h"
3940
#include "flang/Optimizer/Builder/Runtime/Ragged.h"
4041
#include "flang/Optimizer/Builder/Runtime/Stop.h"
4142
#include "flang/Optimizer/Builder/Todo.h"
@@ -359,8 +360,10 @@ class FirConverter : public Fortran::lower::AbstractConverter {
359360
// not need to be generated even if no defaults are specified.
360361
// However, generating main or changing when the runtime reads
361362
// environment variables is required to do so.
362-
fir::runtime::genEnvironmentDefaults(*builder, toLocation(),
363-
bridge.getEnvironmentDefaults());
363+
auto env = fir::runtime::genEnvironmentDefaults(
364+
*builder, toLocation(), bridge.getEnvironmentDefaults());
365+
366+
fir::runtime::genMain(*builder, toLocation(), env);
364367
});
365368

366369
finalizeOpenACCLowering();

flang/lib/Optimizer/Builder/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ add_flang_library(FIRBuilder
2323
Runtime/Execute.cpp
2424
Runtime/Inquiry.cpp
2525
Runtime/Intrinsics.cpp
26+
Runtime/Main.cpp
2627
Runtime/Numeric.cpp
2728
Runtime/Pointer.cpp
2829
Runtime/Ragged.cpp

flang/lib/Optimizer/Builder/Runtime/EnvironmentDefaults.cpp

+3-4
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
#include "flang/Optimizer/Support/InternalNames.h"
1414
#include "llvm/ADT/ArrayRef.h"
1515

16-
void fir::runtime::genEnvironmentDefaults(
16+
fir::GlobalOp fir::runtime::genEnvironmentDefaults(
1717
fir::FirOpBuilder &builder, mlir::Location loc,
1818
const std::vector<Fortran::lower::EnvironmentDefault> &envDefaults) {
1919
std::string envDefaultListPtrName =
@@ -34,14 +34,13 @@ void fir::runtime::genEnvironmentDefaults(
3434

3535
// If no defaults were specified, initialize with a null pointer.
3636
if (envDefaults.empty()) {
37-
builder.createGlobalConstant(
37+
return builder.createGlobalConstant(
3838
loc, envDefaultListRefTy, envDefaultListPtrName,
3939
[&](fir::FirOpBuilder &builder) {
4040
mlir::Value nullVal =
4141
builder.createNullConstant(loc, envDefaultListRefTy);
4242
builder.create<fir::HasValueOp>(loc, nullVal);
4343
});
44-
return;
4544
}
4645

4746
// Create the Item list.
@@ -99,7 +98,7 @@ void fir::runtime::genEnvironmentDefaults(
9998
envDefaultListBuilder, linkOnce);
10099

101100
// Define the pointer to the list used by the runtime.
102-
builder.createGlobalConstant(
101+
return builder.createGlobalConstant(
103102
loc, envDefaultListRefTy, envDefaultListPtrName,
104103
[&](fir::FirOpBuilder &builder) {
105104
mlir::Value addr = builder.create<fir::AddrOfOp>(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
//===-- Main.cpp - generate main runtime API calls --------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "flang/Optimizer/Builder/Runtime/Main.h"
10+
#include "flang/Optimizer/Builder/BoxValue.h"
11+
#include "flang/Optimizer/Builder/FIRBuilder.h"
12+
#include "flang/Optimizer/Builder/Runtime/RTBuilder.h"
13+
#include "flang/Optimizer/Dialect/FIROps.h"
14+
#include "flang/Optimizer/Dialect/FIRType.h"
15+
#include "flang/Runtime/main.h"
16+
#include "flang/Runtime/stop.h"
17+
18+
using namespace Fortran::runtime;
19+
20+
/// Create a `int main(...)` that calls the Fortran entry point
21+
void fir::runtime::genMain(fir::FirOpBuilder &builder, mlir::Location loc,
22+
fir::GlobalOp &env) {
23+
auto *context = builder.getContext();
24+
auto argcTy = builder.getDefaultIntegerType();
25+
auto ptrTy = mlir::LLVM::LLVMPointerType::get(context);
26+
27+
// void ProgramStart(int argc, char** argv, char** envp,
28+
// _QQEnvironmentDefaults* env)
29+
auto startFn = builder.createFunction(
30+
loc, RTNAME_STRING(ProgramStart),
31+
mlir::FunctionType::get(context, {argcTy, ptrTy, ptrTy, ptrTy}, {}));
32+
// void ProgramStop()
33+
auto stopFn =
34+
builder.createFunction(loc, RTNAME_STRING(ProgramEndStatement),
35+
mlir::FunctionType::get(context, {}, {}));
36+
37+
// int main(int argc, char** argv, char** envp)
38+
auto mainFn = builder.createFunction(
39+
loc, "main",
40+
mlir::FunctionType::get(context, {argcTy, ptrTy, ptrTy}, argcTy));
41+
// void _QQmain()
42+
auto qqMainFn = builder.createFunction(
43+
loc, "_QQmain", mlir::FunctionType::get(context, {}, {}));
44+
45+
mainFn.setPublic();
46+
47+
auto *block = mainFn.addEntryBlock();
48+
mlir::OpBuilder::InsertionGuard insertGuard(builder);
49+
builder.setInsertionPointToStart(block);
50+
51+
llvm::SmallVector<mlir::Value, 4> args(block->getArguments());
52+
auto envAddr =
53+
builder.create<fir::AddrOfOp>(loc, env.getType(), env.getSymbol());
54+
args.push_back(envAddr);
55+
56+
builder.create<fir::CallOp>(loc, startFn, args);
57+
builder.create<fir::CallOp>(loc, qqMainFn);
58+
builder.create<fir::CallOp>(loc, stopFn);
59+
60+
mlir::Value ret = builder.createIntegerConstant(loc, argcTy, 0);
61+
builder.create<mlir::func::ReturnOp>(loc, ret);
62+
}

0 commit comments

Comments
 (0)