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
@@ -319,10 +319,24 @@
Fortran::parser::DumpTree(llvm::outs(), parseTree, &asFortran);
}
if (driver.dumpUnparse) {
- Unparse(llvm::outs(), parseTree, driver.encoding, true /*capitalize*/,
+ int fd;
+ llvm::SmallString<32> tmpSourcePath;
+ std::error_code EC =
+ llvm::sys::fs::createUniqueFile(driver.outputPath, fd, tmpSourcePath);
+ if (EC) {
+ llvm::errs() << EC.message() << "\n";
+ std::exit(EXIT_FAILURE);
+ }
+ llvm::raw_fd_ostream tmpSource(fd, /*shouldClose*/ true);
+ // Unparse(llvm::outs(), parseTree, driver.encoding, true /*capitalize*/,
+ // options.features.IsEnabled(
+ // Fortran::common::LanguageFeature::BackslashEscapes),
+ // nullptr /* action before each statement */, &asFortran);
+ Unparse(tmpSource, parseTree, driver.encoding, true /*capitalize*/,
options.features.IsEnabled(
Fortran::common::LanguageFeature::BackslashEscapes),
- nullptr /* action before each statement */, &asFortran);
+ nullptr /* action before each statement */,
+ driver.unparseTypedExprsToF18_FC ? &asFortran : nullptr);
return {};
}
if (driver.dumpPreFirTree) {
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,284 @@
# 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"
+
+# === 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 - lib files extracted from $1 (array, name reference)
+# =============================================================================
+categorise_files()
+{
+ local -n -r all_files=$1
+ local -n sources=$2
+ local -n libs=$3
+
+ 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
+ sources+=($current_file)
+ else
+ libs+=($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 "$@"
+
+ source_files=()
+ lib_files=()
+ categorise_files INPUT_FILES source_files lib_files
+
+ flang_options=()
+ fc_options=()
+ categorise_opts OPTIONS flang_options 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")
+
+ [[ ! -z ${MODULE_DIR} ]] && flang_options+=("-module-dir ${MODULE_DIR}")
+ [[ ! -z ${INTRINSICS_MOD_DIR} ]] && flang_options+=("-intrinsics-module-directory ${INTRINSICS_MOD_DIR}")
+ for idx in "${!source_files[@]}"; do
+ if ! "$wd/bin/f18" "${flang_options[@]}" "${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
+ readonly external_fc="${F18_FC:-gfortran}"
+ object_files=()
+ for idx in "${!source_files[@]}"; do
+ # We need to consider 3 cases here:
+ # * For `flang -c -o `, we need to use `-o `.
+ # * For `flang -c `, we let the compiler decide what the
+ # output file is (i.e., don't use `-o`).
+ # * For `flang `, we need to generate a temporary object file
+ # that will then be linked (i.e., use `-o `).
+ if [[ $COMPILE_ONLY == "True" ]]; then
+ if [[ ${#source_files[@]} -eq 1 ]] && [[ ! -z ${OUTPUT_FILE+x} ]]; then
+ output_definition="-o $OUTPUT_FILE"
+ fi
+ else
+ output_definition="-o ${OUTPUT_FILE}_${idx}"
+ fi
+
+ if ! $external_fc -c "${fc_options[@]}" "${unparsed_file}_${idx}.f90" ${output_definition:+$output_definition}
+ then status=$?
+ echo flang: in "$PWD", f18 failed with exit status $status: "$wd/bin/f18" "${fc_options[@]}" "$@" >&2
+ exit $status
+ fi
+ object_files+=(${OUTPUT_FILE}_${idx})
+ done
+
+ if [[ $COMPILE_ONLY == "True" ]]; then
+ exit 0;
+ fi
+
+ # Delete unparsed files
+ for idx in "${!source_files[@]}"; do
+ rm "${unparsed_file}_${idx}.f90"
+ done
+
+ # STEP 3: Link
+ if [[ ${#object_files[@]} -ge 1 ]] || [[ ${#lib_files[@]} -ge 1 ]] ; then
+ if ! $external_fc "${fc_options[@]}" "${object_files[@]}" "${lib_files[@]}" -o "$OUTPUT_FILE"
+ then status=$?
+ echo flang: in "$PWD", f18 failed with exit status $status: "$wd/bin/f18" "${flang_options[@]}" "$@" >&2
+ exit $status
+ fi
+ fi
+
+ # Delete intermediate object files
+ for idx in "${!source_files[@]}"; do
+ rm "${OUTPUT_FILE}_${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 "${@}"