diff --git a/flang/tools/f18/f18.cpp b/flang/tools/f18/f18.cpp --- a/flang/tools/f18/f18.cpp +++ b/flang/tools/f18/f18.cpp @@ -105,7 +105,7 @@ bool debugModuleWriter{false}; bool defaultReal8{false}; bool measureTree{false}; - bool unparseTypedExprsToF18_FC{false}; + bool unparseTypedExprsToF18_FC{true}; std::vector F18_FCArgs; const char *prefix{nullptr}; bool getDefinition{false}; @@ -187,7 +187,7 @@ }, }; -std::string CompileFortran(std::string path, Fortran::parser::Options options, +void CompileFortran(std::string path, Fortran::parser::Options options, DriverOptions &driver, const Fortran::common::IntrinsicTypeDefaultKinds &defaultKinds) { Fortran::parser::AllSources allSources; @@ -215,21 +215,21 @@ llvm::errs() << driver.prefix << "Could not scan " << path << '\n'; parsing.messages().Emit(llvm::errs(), allCookedSources); exitStatus = EXIT_FAILURE; - return {}; + return; } if (driver.dumpProvenance) { parsing.DumpProvenance(llvm::outs()); - return {}; + return; } if (driver.dumpCookedChars) { parsing.messages().Emit(llvm::errs(), allCookedSources); parsing.DumpCookedChars(llvm::outs()); - return {}; + return; } parsing.Parse(llvm::outs()); if (options.instrumentedParse) { parsing.DumpParsingLog(llvm::outs()); - return {}; + return; } parsing.ClearLog(); parsing.messages().Emit(llvm::errs(), allCookedSources); @@ -237,14 +237,14 @@ parsing.EmitMessage(llvm::errs(), parsing.finalRestingPlace(), "Parser FAIL (final position)"); exitStatus = EXIT_FAILURE; - return {}; + return; } if ((!parsing.messages().empty() && (driver.warningsAreErrors || parsing.messages().AnyFatalError())) || !parsing.parseTree()) { llvm::errs() << driver.prefix << "Could not parse " << path << '\n'; exitStatus = EXIT_FAILURE; - return {}; + return; } auto &parseTree{*parsing.parseTree()}; if (driver.measureTree) { @@ -275,7 +275,7 @@ if (driver.dumpParseTree) { Fortran::parser::DumpTree(llvm::outs(), parseTree, &asFortran); } - return {}; + return; } if (driver.dumpSymbols) { semantics.DumpSymbols(llvm::outs()); @@ -283,11 +283,11 @@ if (driver.dumpUnparseWithSymbols) { Fortran::semantics::UnparseWithSymbols( llvm::outs(), parseTree, driver.encoding); - return {}; + return; } if (driver.getSymbolsSources) { semantics.DumpSymbolsSources(llvm::outs()); - return {}; + return; } if (driver.getDefinition) { if (auto cb{allCookedSources.GetCharBlockFromLineAndColumns( @@ -306,44 +306,23 @@ << sourceInfo->first.column << "-" << sourceInfo->second.column << "\n"; exitStatus = EXIT_SUCCESS; - return {}; + return; } } } llvm::errs() << "Symbol not found.\n"; exitStatus = EXIT_FAILURE; - return {}; + return; } } if (driver.dumpParseTree) { Fortran::parser::DumpTree(llvm::outs(), parseTree, &asFortran); } if (driver.dumpUnparse) { - Unparse(llvm::outs(), parseTree, driver.encoding, true /*capitalize*/, - options.features.IsEnabled( - Fortran::common::LanguageFeature::BackslashEscapes), - nullptr /* action before each statement */, &asFortran); - return {}; - } - if (driver.dumpPreFirTree) { - if (auto ast{Fortran::lower::createPFT(parseTree, semanticsContext)}) { - Fortran::lower::dumpPFT(llvm::outs(), *ast); - } else { - llvm::errs() << "Pre FIR Tree is NULL.\n"; - exitStatus = EXIT_FAILURE; - } - } - if (driver.syntaxOnly) { - return {}; - } - - std::string relo{RelocatableName(driver, path)}; - - llvm::SmallString<32> tmpSourcePath; - { int fd; + llvm::SmallString<32> tmpSourcePath; std::error_code EC = - llvm::sys::fs::createUniqueFile("f18-%%%%.f90", fd, tmpSourcePath); + llvm::sys::fs::createUniqueFile(driver.outputPath, fd, tmpSourcePath); if (EC) { llvm::errs() << EC.message() << "\n"; std::exit(EXIT_FAILURE); @@ -354,14 +333,21 @@ Fortran::common::LanguageFeature::BackslashEscapes), nullptr /* action before each statement */, driver.unparseTypedExprsToF18_FC ? &asFortran : nullptr); + return; } - - RunOtherCompiler(driver, tmpSourcePath.data(), relo.data()); - filesToDelete.emplace_back(tmpSourcePath); - if (!driver.compileOnly && driver.outputPath.empty()) { - filesToDelete.push_back(relo); + if (driver.dumpPreFirTree) { + if (auto ast{Fortran::lower::createPFT(parseTree, semanticsContext)}) { + Fortran::lower::dumpPFT(llvm::outs(), *ast); + } else { + llvm::errs() << "Pre FIR Tree is NULL.\n"; + exitStatus = EXIT_FAILURE; + } } - return relo; + if (driver.syntaxOnly) { + return; + } + + return; } std::string CompileOtherLanguage(std::string path, DriverOptions &driver) { @@ -578,8 +564,8 @@ } else if (arg == "-funparse-with-symbols" || arg == "-fdebug-unparse-with-symbols") { driver.dumpUnparseWithSymbols = true; - } else if (arg == "-funparse-typed-exprs-to-f18-fc") { - driver.unparseTypedExprsToF18_FC = true; + } else if (arg == "-fno-unparse-typed-exprs-to-f18-fc") { + driver.unparseTypedExprsToF18_FC = false; } else if (arg == "-fparse-only" || arg == "-fsyntax-only") { driver.syntaxOnly = true; } else if (arg == "-c") { @@ -805,19 +791,7 @@ } } } - std::string relo{CompileFortran(path, options, driver, defaultKinds)}; - if (!driver.compileOnly && !relo.empty()) { - objlist.push_back(relo); - } - } - for (const auto &path : otherSources) { - std::string relo{CompileOtherLanguage(path, driver)}; - if (!driver.compileOnly && !relo.empty()) { - objlist.push_back(relo); - } - } - if (!driver.compileOnly && !objlist.empty()) { - Link(liblist, objlist, driver); + CompileFortran(path, options, driver, defaultKinds); } return exitStatus; } diff --git a/flang/tools/f18/flang b/flang/tools/f18/flang old mode 100644 new mode 100755 --- a/flang/tools/f18/flang +++ b/flang/tools/f18/flang @@ -1,4 +1,4 @@ -#!/bin/bash +#! /usr/bin/env bash #===-- tools/f18/flang.sh -----------------------------------------*- sh -*-===# # # Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. @@ -6,11 +6,311 @@ # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception # #===------------------------------------------------------------------------===# +# A wrapper script for Flang's compiler driver that was developed for testing and +# experimenting. You should be able to use it as a regular compiler driver. It +# will: +# * run Flang's compiler driver to unparse the input source files +# * use the external compiler (defined via F18_FC environment variable) to +# compile the unparsed source files +#===------------------------------------------------------------------------===# +set -euo pipefail + +# Global variables to make the parsing of input arguments a bit easier +INPUT_FILES=() +OPTIONS=() +OUTPUT_FILE="" +MODULE_DIR="" +INTRINSICS_MOD_DIR="" +COMPILE_ONLY="False" +TEMP_OUTPUT="flang_temp_out_" + +# === parse_args ============================================================== +# +# Parse the input arguments passed to this script. Sets the global variables +# declared at the top. +# +# INPUTS: +# $1 - all input arguments +# OUTPUTS: +# Saved in the global variables for this script +# ============================================================================= +parse_args() +{ + while [ "${1:-}" != "" ]; do + # CASE 1: Compiler option + if [[ "${1:0:1}" == "-" ]] ; then + # Output file - extract it into a global variable + if [[ "$1" == "-o" ]] ; then + shift + OUTPUT_FILE="$1" + shift + continue + fi + + # Module directory - extract it into a global variable + if [[ "$1" == "-module-dir" ]]; then + shift + MODULE_DIR="$1" + shift + continue + fi + + # Intrinsics module dir - extract it into a global var + if [[ "$1" == "-intrinsics-module-directory" ]]; then shift + INTRINSICS_MOD_DIR=$1 + shift + continue + fi + + # Module suffix cannot be modified - this script defines it before + # calling the driver. + if [[ "$1" == "-module-suffix" ]]; then + echo "ERROR: \'-module-suffix\' is not available when using the \'flang\' script" + exit 1 + fi + + # Special treatment for `J ` and `-I `. We translate these + # into `J` and `-I` respectively. + if [[ "$1" == "-J" ]] || [[ "$1" == "-I" ]]; then + opt=$1 + shift + OPTIONS+=("$opt$1") + shift + continue + fi + + # This is a regular option - just add it to the list. + OPTIONS+=($1) + if [[ $1 == "-c" ]]; then + COMPILE_ONLY="True" + fi + shift + continue + + # CASE 2: A regular file (either source or a library file) + elif [[ -f "$1" ]]; then + INPUT_FILES+=($1) + shift + continue + + else + # CASE 3: Unsupported + echo "ERROR: unrecognised option format $1" + exit 1 + fi + done +} + +# === categorise_files ======================================================== +# +# Categorises input files into: +# * Fortran source files (to be compiled) +# * library files (to be linked into the final executable) +# +# INPUTS: +# $1 - all input files to be categorised (array, name reference) +# OUTPUTS: +# $2 - Fortran source files extracted from $1 (array, name reference) +# $3 - other source files extracted from $1 (array, name reference) +# $4 - object files extracted from $1 (array, name reference) +# $4 - lib files extracted from $1 (array, name reference) +# ============================================================================= +categorise_files() +{ + local -n -r all_files=$1 + local -n fortran_sources=$2 + local -n other_sources=$3 + local -n libs=$4 + + for current_file in "${all_files[@]}"; do + file_ext=${current_file##*.} + if [[ $file_ext == "f" ]] || [[ $file_ext == "f90" ]] || + [[ $file_ext == "f" ]] || [[ $file_ext == "F" ]] || [[ $file_ext == "ff" ]] || + [[ $file_ext == "f90" ]] || [[ $file_ext == "F90" ]] || [[ $file_ext == "ff90" ]] || + [[ $file_ext == "f95" ]] || [[ $file_ext == "F95" ]] || [[ $file_ext == "ff95" ]] || + [[ $file_ext == "cuf" ]] || [[ $file_ext == "CUF" ]] || [[ $file_ext == "f18" ]] || + [[ $file_ext == "F18" ]] || [[ $file_ext == "ff18" ]]; then + fortran_sources+=($current_file) + elif [[ $file_ext == "a" ]] || [[ $file_ext == "so" ]]; then + libs+=($current_file) + elif [[ $file_ext == "o" ]]; then + object_files+=($current_file) + else + other_sources+=($current_file) + fi + done +} + +# === categorise_opts ========================================================== +# +# Categorises compiler options into options for: +# * the Flang driver (either new or the "throwaway" driver) +# * the external Fortran driver that will generate the code +# Most options accepted by Flang will be claimed by it. The only exceptions are +# `-I` and `-J`. +# +# INPUTS: +# $1 - all compiler options (array, name reference) +# OUTPUTS: +# $2 - compiler options for the Flang driver (array, name reference) +# $3 - compiler options for the external driver (array, name reference) +# ============================================================================= +categorise_opts() +{ + local -n all_opts=$1 + local -n flang_opts=$2 + local -n fc_opts=$3 + + for opt in "${all_opts[@]}"; do + # These options are claimed by Flang, but should've been dealt with in parse_args. + if [[ $opt == "-module-dir" ]] || + [[ $opt == "-o" ]] || + [[ $opt == "-fintrinsic-modules-path" ]] ; then + echo "ERROR: $opt should've been fully processed by \`parse_args\`" + exit 1 + fi + + if + # The options claimed by Flang. This list needs to be compatible with + # what's supported by Flang's frontend driver (i.e. `flang-new -fc1` and f18). + [[ $opt == "-cpp" ]] || + [[ $opt =~ ^-D.* ]] || + [[ $opt == "-E" ]] || + [[ $opt == "-falternative-parameter-statement" ]] || + [[ $opt == "-fbackslash" ]] || + [[ $opt == "-fcolor-diagnostics" ]] || + [[ $opt == "-fdefault-double-8" ]] || + [[ $opt == "-fdefault-integer-8" ]] || + [[ $opt == "-fdefault-real-8" ]] || + [[ $opt == "-ffixed-form" ]] || + [[ $opt =~ ^-ffixed-line-length=.* ]] || + [[ $opt == "-ffree-form" ]] || + [[ $opt == "-fimplicit-none" ]] || + [[ $opt =~ ^-finput-charset=.* ]] || + [[ $opt == "-flarge-sizes" ]] || + [[ $opt == "-flogical-abbreviations" ]] || + [[ $opt == "-fno-color-diagnostics" ]] || + [[ $opt == "-fopenacc" ]] || + [[ $opt == "-fopenmp" ]] || + [[ $opt == "-fxor-operator" ]] || + [[ $opt == "-help" ]] || + [[ $opt == "-nocpp" ]] || + [[ $opt == "-pedantic" ]] || + [[ $opt =~ ^-std=.* ]] || + [[ $opt =~ ^-U.* ]] || + [[ $opt == "--version" ]] || + [[ $opt == "-Werror" ]]; then + flang_opts+=($opt) + elif [[ $opt =~ -I.* ]] || [[ $opt =~ -J.* ]]; then + # Options that are needed for both Flang and the external driver. + flang_opts+=($opt) + fc_opts+=($opt) + else + # All other options are claimed for the external driver. + fc_opts+=($opt) + fi + done +} + +# === main ==================================================================== +# Main entry point for this script +# ============================================================================= +main() { + parse_args "$@" + + fortran_source_files=() + other_source_files=() + object_files=() + lib_files=() + categorise_files INPUT_FILES fortran_source_files other_source_files object_files lib_files + + flang_options=() + ext_fc_options=() + categorise_opts OPTIONS flang_options ext_fc_options + + local -r wd=$(cd "$(dirname "$0")/.." && pwd) + + # STEP 1: Unparse + local -r unparsed_file="flang_unparsed_source_file" + flang_options+=("-module-suffix") + flang_options+=(".f18.mod") + flang_options+=("-fdebug-unparse") + flang_options+=("-fno-unparse-typed-exprs-to-f18-fc") + + [[ ! -z ${MODULE_DIR} ]] && flang_options+=("-module-dir ${MODULE_DIR}") + [[ ! -z ${INTRINSICS_MOD_DIR} ]] && flang_options+=("-intrinsics-module-directory ${INTRINSICS_MOD_DIR}") + for idx in "${!fortran_source_files[@]}"; do + if ! "$wd/bin/f18" "${flang_options[@]}" "${fortran_source_files[$idx]}" -o "${unparsed_file}_${idx}.f90" + then status=$? + echo flang: in "$PWD", f18 failed with exit status $status: "$wd/bin/f18" "${flang_options[@]}" "$@" >&2 + exit $status + fi + done + + # STEP 2: Compile Fortran Source Files + readonly ext_fc="${F18_FC:-gfortran}" + for idx in "${!fortran_source_files[@]}"; do + # Use the value of $OUTPUT_FILE for the output file iff `-c` was used. + if [[ ! -z ${OUTPUT_FILE:+x} ]] && [[ $COMPILE_ONLY == "True" ]]; then + output_definition="-o $OUTPUT_FILE" + elif [[ $COMPILE_ONLY == "False" ]]; then + output_definition="-o ${TEMP_OUTPUT}_${idx}" + fi + + if ! $ext_fc -c "${ext_fc_options[@]}" "${unparsed_file}_${idx}.f90" ${output_definition:+$output_definition} + then status=$? + echo flang: in "$PWD", "$ext_fc" failed with exit status $status: "$ext_fc" "${ext_fc_options[@]}" "$@" >&2 + exit $status + fi + object_files+=(${TEMP_OUTPUT}_${idx}) + done + + # Delete the unparsed files + for idx in "${!fortran_source_files[@]}"; do + rm "${unparsed_file}_${idx}.f90" + done + + # STEP 3: Compile Other Source Files + for idx in "${!other_source_files[@]}"; do + # Use the value of $OUTPUT_FILE for the output file iff `-c` was used. + if [[ ! -z ${OUTPUT_FILE:+x} ]] && [[ $COMPILE_ONLY == "True" ]]; then + output_definition="-o $OUTPUT_FILE" + elif [[ $COMPILE_ONLY == "False" ]]; then + output_definition="-o ${TEMP_OUTPUT}_${idx}" + fi + + if ! $ext_fc -c "${ext_fc_options[@]}" "${other_source_files[${idx}]}" ${output_definition:+$output_definition} + then status=$? + echo flang: in "$PWD", "$ext_fc" failed with exit status $status: "$ext_fc" "${ext_fc_options[@]}" "$@" >&2 + exit $status + fi + object_files+=(${TEMP_OUTPUT}_${idx}) + done + + # STEP 4: Link + if [[ $COMPILE_ONLY == "True" ]]; then + exit 0; + fi + + if [[ ${#object_files[@]} -ge 1 ]]; then + # If $OUTPUT_FILE was specified, use it for the output name. + if [[ ! -z ${OUTPUT_FILE:+x} ]]; then + output_definition="-o $OUTPUT_FILE" + else + output_definition="" + fi + + if ! $ext_fc "${ext_fc_options[@]}" "${object_files[@]}" "${lib_files[@]}" ${output_definition:+$output_definition} + then status=$? + echo flang: in "$PWD", "$ext_fc" failed with exit status $status: "$ext_fc" "${ext_fc_options[@]}" "$@" >&2 + exit $status + fi + fi + + # Delete intermediate object files + for idx in "${!fortran_source_files[@]}"; do + rm "${TEMP_OUTPUT}_${idx}" + done +} -wd=$(cd $(dirname "$0")/.. && pwd) -opts="-module-suffix .f18.mod " -if ! $wd/bin/f18 $opts "$@" -then status=$? - echo flang: in $PWD, f18 failed with exit status $status: $wd/bin/f18 $opts "$@" >&2 - exit $status -fi +main "${@}"