1 //===-- tools/f18/f18.cpp -------------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 // Temporary Fortran front end driver main program for development scaffolding.
11 #include "flang/Common/Fortran-features.h"
12 #include "flang/Common/default-kinds.h"
13 #include "flang/Evaluate/expression.h"
14 #include "flang/Lower/PFTBuilder.h"
15 #include "flang/Parser/characters.h"
16 #include "flang/Parser/dump-parse-tree.h"
17 #include "flang/Parser/message.h"
18 #include "flang/Parser/parse-tree-visitor.h"
19 #include "flang/Parser/parse-tree.h"
20 #include "flang/Parser/parsing.h"
21 #include "flang/Parser/provenance.h"
22 #include "flang/Parser/unparse.h"
23 #include "flang/Semantics/expression.h"
24 #include "flang/Semantics/semantics.h"
25 #include "flang/Semantics/unparse-with-symbols.h"
26 #include "llvm/Support/raw_ostream.h"
41 static std::list<std::string> argList(int argc, char *const argv[]) {
42 std::list<std::string> result;
43 for (int j = 0; j < argc; ++j) {
44 result.emplace_back(argv[j]);
49 struct MeasurementVisitor {
50 template<typename A> bool Pre(const A &) { return true; }
51 template<typename A> void Post(const A &) {
55 size_t objects{0}, bytes{0};
58 void MeasureParseTree(const Fortran::parser::Program &program) {
59 MeasurementVisitor visitor;
60 Fortran::parser::Walk(program, visitor);
61 std::cout << "Parse tree comprises " << visitor.objects
62 << " objects and occupies " << visitor.bytes << " total bytes.\n";
65 std::vector<std::string> filesToDelete;
67 void CleanUpAtExit() {
68 for (const auto &path : filesToDelete) {
75 struct GetDefinitionArgs {
76 int line, startColumn, endColumn;
79 struct DriverOptions {
81 bool verbose{false}; // -v
82 bool compileOnly{false}; // -c
83 std::string outputPath; // -o path
84 std::vector<std::string> searchDirectories{"."s}; // -I dir
85 std::string moduleDirectory{"."s}; // -module dir
86 std::string moduleFileSuffix{".mod"}; // -moduleSuffix suff
87 bool forcedForm{false}; // -Mfixed or -Mfree appeared
88 bool warnOnNonstandardUsage{false}; // -Mstandard
89 bool warningsAreErrors{false}; // -Werror
90 Fortran::parser::Encoding encoding{Fortran::parser::Encoding::UTF_8};
91 bool parseOnly{false};
92 bool dumpProvenance{false};
93 bool dumpCookedChars{false};
94 bool dumpUnparse{false};
95 bool dumpUnparseWithSymbols{false};
96 bool dumpParseTree{false};
97 bool dumpPreFirTree{false};
98 bool dumpSymbols{false};
99 bool debugResolveNames{false};
100 bool debugNoSemantics{false};
101 bool measureTree{false};
102 bool unparseTypedExprsToPGF90{false};
103 std::vector<std::string> pgf90Args;
104 const char *prefix{nullptr};
105 bool getDefinition{false};
106 GetDefinitionArgs getDefinitionArgs{0, 0, 0};
107 bool getSymbolsSources{false};
110 bool ParentProcess() {
112 return false; // in child process
116 if (!WIFEXITED(childStat) || WEXITSTATUS(childStat) != 0) {
122 void Exec(std::vector<char *> &argv, bool verbose = false) {
124 for (size_t j{0}; j < argv.size(); ++j) {
125 std::cerr << (j > 0 ? " " : "") << argv[j];
129 argv.push_back(nullptr);
130 execvp(argv[0], &argv[0]);
131 std::cerr << "execvp(" << argv[0] << ") failed: " << std::strerror(errno)
136 void RunOtherCompiler(DriverOptions &driver, char *source, char *relo) {
137 std::vector<char *> argv;
138 for (size_t j{0}; j < driver.pgf90Args.size(); ++j) {
139 argv.push_back(driver.pgf90Args[j].data());
141 char dashC[3] = "-c", dashO[3] = "-o";
142 argv.push_back(dashC);
143 argv.push_back(dashO);
144 argv.push_back(relo);
145 argv.push_back(source);
146 Exec(argv, driver.verbose);
149 std::string RelocatableName(const DriverOptions &driver, std::string path) {
150 if (driver.compileOnly && !driver.outputPath.empty()) {
151 return driver.outputPath;
153 std::string base{path};
154 auto slash{base.rfind("/")};
155 if (slash != std::string::npos) {
156 base = base.substr(slash + 1);
158 std::string relo{base};
159 auto dot{base.rfind(".")};
160 if (dot != std::string::npos) {
161 relo = base.substr(0, dot);
167 int exitStatus{EXIT_SUCCESS};
169 static Fortran::parser::AnalyzedObjectsAsFortran asFortran{
170 [](std::ostream &o, const Fortran::evaluate::GenericExprWrapper &x) {
174 o << "(bad expression)";
177 [](std::ostream &o, const Fortran::evaluate::GenericAssignmentWrapper &x) {
181 o << "(bad assignment)";
184 [](std::ostream &o, const Fortran::evaluate::ProcedureRef &x) {
185 x.AsFortran(o << "CALL ");
189 std::string CompileFortran(std::string path, Fortran::parser::Options options,
190 DriverOptions &driver,
191 const Fortran::common::IntrinsicTypeDefaultKinds &defaultKinds) {
192 Fortran::parser::AllSources allSources;
193 allSources.set_encoding(driver.encoding);
194 Fortran::semantics::SemanticsContext semanticsContext{
195 defaultKinds, options.features, allSources};
196 semanticsContext.set_moduleDirectory(driver.moduleDirectory)
197 .set_moduleFileSuffix(driver.moduleFileSuffix)
198 .set_searchDirectories(driver.searchDirectories)
199 .set_warnOnNonstandardUsage(driver.warnOnNonstandardUsage)
200 .set_warningsAreErrors(driver.warningsAreErrors);
201 if (!driver.forcedForm) {
202 auto dot{path.rfind(".")};
203 if (dot != std::string::npos) {
204 std::string suffix{path.substr(dot + 1)};
205 options.isFixedForm = suffix == "f" || suffix == "F" || suffix == "ff";
208 options.searchDirectories = driver.searchDirectories;
209 Fortran::parser::Parsing parsing{semanticsContext.allSources()};
210 parsing.Prescan(path, options);
211 if (!parsing.messages().empty() &&
212 (driver.warningsAreErrors || parsing.messages().AnyFatalError())) {
213 std::cerr << driver.prefix << "could not scan " << path << '\n';
214 parsing.messages().Emit(std::cerr, parsing.cooked());
215 exitStatus = EXIT_FAILURE;
218 if (driver.dumpProvenance) {
219 parsing.DumpProvenance(std::cout);
222 if (driver.dumpCookedChars) {
223 parsing.messages().Emit(std::cerr, parsing.cooked());
224 parsing.DumpCookedChars(std::cout);
227 parsing.Parse(&std::cout);
228 if (options.instrumentedParse) {
229 parsing.DumpParsingLog(std::cout);
233 parsing.messages().Emit(std::cerr, parsing.cooked());
234 if (!parsing.consumedWholeFile()) {
236 std::cerr, parsing.finalRestingPlace(), "parser FAIL (final position)");
237 exitStatus = EXIT_FAILURE;
240 if ((!parsing.messages().empty() &&
241 (driver.warningsAreErrors || parsing.messages().AnyFatalError())) ||
242 !parsing.parseTree()) {
243 std::cerr << driver.prefix << "could not parse " << path << '\n';
244 exitStatus = EXIT_FAILURE;
247 auto &parseTree{*parsing.parseTree()};
248 if (driver.measureTree) {
249 MeasureParseTree(parseTree);
251 if (!driver.debugNoSemantics || driver.debugResolveNames ||
252 driver.dumpSymbols || driver.dumpUnparseWithSymbols ||
253 driver.getDefinition || driver.getSymbolsSources) {
254 Fortran::semantics::Semantics semantics{
255 semanticsContext, parseTree, parsing.cooked()};
257 semantics.EmitMessages(std::cerr);
258 if (driver.dumpSymbols) {
259 semantics.DumpSymbols(std::cout);
261 if (semantics.AnyFatalError()) {
262 std::cerr << driver.prefix << "semantic errors in " << path << '\n';
263 exitStatus = EXIT_FAILURE;
264 if (driver.dumpParseTree) {
265 Fortran::parser::DumpTree(std::cout, parseTree, &asFortran);
269 if (driver.dumpUnparseWithSymbols) {
270 Fortran::semantics::UnparseWithSymbols(
271 std::cout, parseTree, driver.encoding);
274 if (driver.getSymbolsSources) {
275 semantics.DumpSymbolsSources(std::cout);
278 if (driver.getDefinition) {
279 if (auto cb{parsing.cooked().GetCharBlockFromLineAndColumns(
280 driver.getDefinitionArgs.line,
281 driver.getDefinitionArgs.startColumn,
282 driver.getDefinitionArgs.endColumn)}) {
283 std::cerr << "String range: >" << cb->ToString() << "<\n";
284 if (auto symbol{semanticsContext.FindScope(*cb).FindSymbol(*cb)}) {
285 std::cerr << "Found symbol name: " << symbol->name().ToString()
288 parsing.cooked().GetSourcePositionRange(symbol->name())}) {
289 std::cout << symbol->name().ToString() << ": "
290 << sourceInfo->first.file.path() << ", "
291 << sourceInfo->first.line << ", "
292 << sourceInfo->first.column << "-"
293 << sourceInfo->second.column << "\n";
294 exitStatus = EXIT_SUCCESS;
299 std::cerr << "Symbol not found.\n";
300 exitStatus = EXIT_FAILURE;
304 if (driver.dumpParseTree) {
305 Fortran::parser::DumpTree(std::cout, parseTree, &asFortran);
307 if (driver.dumpUnparse) {
308 Unparse(std::cout, parseTree, driver.encoding, true /*capitalize*/,
309 options.features.IsEnabled(
310 Fortran::common::LanguageFeature::BackslashEscapes),
311 nullptr /* action before each statement */, &asFortran);
314 if (driver.dumpPreFirTree) {
315 if (auto ast{Fortran::lower::createPFT(parseTree)}) {
316 Fortran::lower::annotateControl(*ast);
317 Fortran::lower::dumpPFT(llvm::outs(), *ast);
319 std::cerr << "Pre FIR Tree is NULL.\n";
320 exitStatus = EXIT_FAILURE;
323 if (driver.parseOnly) {
327 std::string relo{RelocatableName(driver, path)};
329 char tmpSourcePath[32];
330 std::snprintf(tmpSourcePath, sizeof tmpSourcePath, "/tmp/f18-%lx.f90",
331 static_cast<unsigned long>(getpid()));
333 std::ofstream tmpSource;
334 tmpSource.open(tmpSourcePath);
335 Fortran::evaluate::formatForPGF90 = true;
336 Unparse(tmpSource, parseTree, driver.encoding, true /*capitalize*/,
337 options.features.IsEnabled(
338 Fortran::common::LanguageFeature::BackslashEscapes),
339 nullptr /* action before each statement */,
340 driver.unparseTypedExprsToPGF90 ? &asFortran : nullptr);
341 Fortran::evaluate::formatForPGF90 = false;
344 if (ParentProcess()) {
345 filesToDelete.push_back(tmpSourcePath);
346 if (!driver.compileOnly && driver.outputPath.empty()) {
347 filesToDelete.push_back(relo);
351 RunOtherCompiler(driver, tmpSourcePath, relo.data());
355 std::string CompileOtherLanguage(std::string path, DriverOptions &driver) {
356 std::string relo{RelocatableName(driver, path)};
357 if (ParentProcess()) {
358 if (!driver.compileOnly && driver.outputPath.empty()) {
359 filesToDelete.push_back(relo);
363 RunOtherCompiler(driver, path.data(), relo.data());
367 void Link(std::vector<std::string> &relocatables, DriverOptions &driver) {
368 if (!ParentProcess()) {
369 std::vector<char *> argv;
370 for (size_t j{0}; j < driver.pgf90Args.size(); ++j) {
371 argv.push_back(driver.pgf90Args[j].data());
373 for (auto &relo : relocatables) {
374 argv.push_back(relo.data());
376 if (!driver.outputPath.empty()) {
377 char dashO[3] = "-o";
378 argv.push_back(dashO);
379 argv.push_back(driver.outputPath.data());
381 Exec(argv, driver.verbose);
385 int main(int argc, char *const argv[]) {
387 atexit(CleanUpAtExit);
389 DriverOptions driver;
390 const char *pgf90{getenv("F18_FC")};
391 driver.pgf90Args.push_back(pgf90 ? pgf90 : "pgf90");
392 bool isPGF90{driver.pgf90Args.back().rfind("pgf90") != std::string::npos};
394 std::list<std::string> args{argList(argc, argv)};
395 std::string prefix{args.front()};
398 driver.prefix = prefix.data();
400 Fortran::parser::Options options;
401 options.predefinitions.emplace_back("__F18", "1");
402 options.predefinitions.emplace_back("__F18_MAJOR__", "1");
403 options.predefinitions.emplace_back("__F18_MINOR__", "1");
404 options.predefinitions.emplace_back("__F18_PATCHLEVEL__", "1");
406 options.predefinitions.emplace_back("__x86_64__", "1");
409 Fortran::common::IntrinsicTypeDefaultKinds defaultKinds;
411 std::vector<std::string> fortranSources, otherSources, relocatables;
412 bool anyFiles{false};
414 while (!args.empty()) {
415 std::string arg{std::move(args.front())};
418 } else if (arg.at(0) != '-') {
420 auto dot{arg.rfind(".")};
421 if (dot == std::string::npos) {
422 driver.pgf90Args.push_back(arg);
424 std::string suffix{arg.substr(dot + 1)};
425 if (suffix == "f" || suffix == "F" || suffix == "ff" ||
426 suffix == "f90" || suffix == "F90" || suffix == "ff90" ||
427 suffix == "f95" || suffix == "F95" || suffix == "ff95" ||
428 suffix == "cuf" || suffix == "CUF" || suffix == "f18" ||
429 suffix == "F18" || suffix == "ff18") {
430 fortranSources.push_back(arg);
431 } else if (suffix == "o" || suffix == "a") {
432 relocatables.push_back(arg);
434 otherSources.push_back(arg);
437 } else if (arg == "-") {
438 fortranSources.push_back("-");
439 } else if (arg == "--") {
440 while (!args.empty()) {
441 fortranSources.emplace_back(std::move(args.front()));
445 } else if (arg == "-Mfixed") {
446 driver.forcedForm = true;
447 options.isFixedForm = true;
448 } else if (arg == "-Mfree") {
449 driver.forcedForm = true;
450 options.isFixedForm = false;
451 } else if (arg == "-Mextend") {
452 options.fixedFormColumns = 132;
453 } else if (arg == "-Munlimited") {
454 // For reparsing f18's -E output of fixed-form cooked character stream
455 options.fixedFormColumns = 1000000;
456 } else if (arg == "-Mbackslash") {
457 options.features.Enable(
458 Fortran::common::LanguageFeature::BackslashEscapes, false);
459 } else if (arg == "-Mnobackslash") {
460 options.features.Enable(
461 Fortran::common::LanguageFeature::BackslashEscapes, true);
462 } else if (arg == "-Mstandard") {
463 driver.warnOnNonstandardUsage = true;
464 } else if (arg == "-fopenmp") {
465 options.features.Enable(Fortran::common::LanguageFeature::OpenMP);
466 options.predefinitions.emplace_back("_OPENMP", "201511");
467 } else if (arg == "-Werror") {
468 driver.warningsAreErrors = true;
469 } else if (arg == "-ed") {
470 options.features.Enable(Fortran::common::LanguageFeature::OldDebugLines);
471 } else if (arg == "-E") {
472 driver.dumpCookedChars = true;
473 } else if (arg == "-fbackslash" || arg == "-fno-backslash") {
474 options.features.Enable(
475 Fortran::common::LanguageFeature::BackslashEscapes,
476 arg == "-fbackslash");
477 } else if (arg == "-fxor-operator" || arg == "-fno-xor-operator") {
478 options.features.Enable(Fortran::common::LanguageFeature::XOROperator,
479 arg == "-fxor-operator");
480 } else if (arg == "-flogical-abbreviations" ||
481 arg == "-fno-logical-abbreviations") {
482 options.features.Enable(
483 Fortran::parser::LanguageFeature::LogicalAbbreviations,
484 arg == "-flogical-abbreviations");
485 } else if (arg == "-fdebug-dump-provenance") {
486 driver.dumpProvenance = true;
487 options.needProvenanceRangeToCharBlockMappings = true;
488 } else if (arg == "-fdebug-dump-parse-tree") {
489 driver.dumpParseTree = true;
490 } else if (arg == "-fdebug-pre-fir-tree") {
491 driver.dumpPreFirTree = true;
492 } else if (arg == "-fdebug-dump-symbols") {
493 driver.dumpSymbols = true;
494 } else if (arg == "-fdebug-resolve-names") {
495 driver.debugResolveNames = true;
496 } else if (arg == "-fdebug-measure-parse-tree") {
497 driver.measureTree = true;
498 } else if (arg == "-fdebug-instrumented-parse") {
499 options.instrumentedParse = true;
500 } else if (arg == "-fdebug-semantics") {
501 } else if (arg == "-fdebug-no-semantics") {
502 driver.debugNoSemantics = true;
503 } else if (arg == "-funparse") {
504 driver.dumpUnparse = true;
505 } else if (arg == "-funparse-with-symbols") {
506 driver.dumpUnparseWithSymbols = true;
507 } else if (arg == "-funparse-typed-exprs-to-pgf90") {
508 driver.unparseTypedExprsToPGF90 = true;
509 } else if (arg == "-fparse-only") {
510 driver.parseOnly = true;
511 } else if (arg == "-c") {
512 driver.compileOnly = true;
513 } else if (arg == "-o") {
514 driver.outputPath = args.front();
516 } else if (arg.substr(0, 2) == "-D") {
517 auto eq{arg.find('=')};
518 if (eq == std::string::npos) {
519 options.predefinitions.emplace_back(arg.substr(2), "1");
521 options.predefinitions.emplace_back(
522 arg.substr(2, eq - 2), arg.substr(eq + 1));
524 } else if (arg.substr(0, 2) == "-U") {
525 options.predefinitions.emplace_back(
526 arg.substr(2), std::optional<std::string>{});
527 } else if (arg == "-r8" || arg == "-fdefault-real-8") {
528 defaultKinds.set_defaultRealKind(8);
529 } else if (arg == "-i8" || arg == "-fdefault-integer-8") {
530 defaultKinds.set_defaultIntegerKind(8);
531 defaultKinds.set_subscriptIntegerKind(8);
532 defaultKinds.set_sizeIntegerKind(8);
533 } else if (arg == "-Mlargearray") {
534 } else if (arg == "-Mnolargearray") {
535 } else if (arg == "-flarge-sizes") {
536 defaultKinds.set_sizeIntegerKind(8);
537 } else if (arg == "-fno-large-sizes") {
538 defaultKinds.set_sizeIntegerKind(4);
539 } else if (arg == "-module") {
540 driver.moduleDirectory = args.front();
542 } else if (arg == "-module-suffix") {
543 driver.moduleFileSuffix = args.front();
545 } else if (arg == "-intrinsic-module-directory") {
546 driver.searchDirectories.push_back(args.front());
548 } else if (arg == "-futf-8") {
549 driver.encoding = Fortran::parser::Encoding::UTF_8;
550 } else if (arg == "-flatin") {
551 driver.encoding = Fortran::parser::Encoding::LATIN_1;
552 } else if (arg == "-fget-definition") {
553 // Receives 3 arguments: line, startColumn, endColumn.
554 options.needProvenanceRangeToCharBlockMappings = true;
555 driver.getDefinition = true;
558 for (int i = 0; i < 3; i++) {
560 std::cerr << "Must provide 3 arguments for -fget-definitions.\n";
563 arguments[i] = std::strtol(args.front().c_str(), &endptr, 10);
564 if (*endptr != '\0') {
565 std::cerr << "Invalid argument to -fget-definitions: " << args.front()
571 driver.getDefinitionArgs = {arguments[0], arguments[1], arguments[2]};
572 } else if (arg == "-fget-symbols-sources") {
573 driver.getSymbolsSources = true;
574 } else if (arg == "-help" || arg == "--help" || arg == "-?") {
577 << " -Mfixed | -Mfree force the source form\n"
578 << " -Mextend 132-column fixed form\n"
579 << " -f[no-]backslash enable[disable] \\escapes in literals\n"
580 << " -M[no]backslash disable[enable] \\escapes in literals\n"
581 << " -Mstandard enable conformance warnings\n"
582 << " -fenable=<feature> enable a language feature\n"
583 << " -fdisable=<feature> disable a language feature\n"
584 << " -r8 | -fdefault-real-8 | -i8 | -fdefault-integer-8 "
585 "change default kinds of intrinsic types\n"
586 << " -Werror treat warnings as errors\n"
587 << " -ed enable fixed form D lines\n"
588 << " -E prescan & preprocess only\n"
589 << " -module dir module output directory (default .)\n"
590 << " -flatin interpret source as Latin-1 (ISO 8859-1) "
591 "rather than UTF-8\n"
592 << " -fparse-only parse only, no output except messages\n"
593 << " -funparse parse & reformat only, no code "
595 << " -funparse-with-symbols parse, resolve symbols, and unparse\n"
596 << " -fdebug-measure-parse-tree\n"
597 << " -fdebug-dump-provenance\n"
598 << " -fdebug-dump-parse-tree\n"
599 << " -fdebug-dump-symbols\n"
600 << " -fdebug-resolve-names\n"
601 << " -fdebug-instrumented-parse\n"
602 << " -fdebug-no-semantics disable semantic checks\n"
603 << " -fget-definition\n"
604 << " -fget-symbols-sources\n"
605 << " -v -c -o -I -D -U have their usual meanings\n"
606 << " -help print this again\n"
607 << "Other options are passed through to the compiler.\n";
609 } else if (arg == "-V") {
610 std::cerr << "\nf18 compiler (under development)\n";
613 driver.pgf90Args.push_back(arg);
615 driver.verbose = true;
616 } else if (arg == "-I") {
617 driver.pgf90Args.push_back(args.front());
618 driver.searchDirectories.push_back(args.front());
620 } else if (arg.substr(0, 2) == "-I") {
621 driver.searchDirectories.push_back(arg.substr(2));
626 if (driver.warnOnNonstandardUsage) {
627 options.features.WarnOnAllNonstandard();
629 if (options.features.IsEnabled(Fortran::common::LanguageFeature::OpenMP)) {
630 driver.pgf90Args.push_back("-mp");
633 if (!options.features.IsEnabled(
634 Fortran::common::LanguageFeature::BackslashEscapes)) {
635 driver.pgf90Args.push_back(
636 "-Mbackslash"); // yes, this *disables* them in pgf90
638 Fortran::parser::useHexadecimalEscapeSequences = false;
640 if (options.features.IsEnabled(
641 Fortran::common::LanguageFeature::BackslashEscapes)) {
642 driver.pgf90Args.push_back("-fbackslash");
644 Fortran::parser::useHexadecimalEscapeSequences = true;
648 driver.measureTree = true;
649 driver.dumpUnparse = true;
650 CompileFortran("-", options, driver, defaultKinds);
653 for (const auto &path : fortranSources) {
654 std::string relo{CompileFortran(path, options, driver, defaultKinds)};
655 if (!driver.compileOnly && !relo.empty()) {
656 relocatables.push_back(relo);
659 for (const auto &path : otherSources) {
660 std::string relo{CompileOtherLanguage(path, driver)};
661 if (!driver.compileOnly && !relo.empty()) {
662 relocatables.push_back(relo);
665 if (!relocatables.empty()) {
666 Link(relocatables, driver);