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 "${@}"