Skip to content

[flang] Treat pre-processed input as fixed #117563

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Dec 3, 2024

Conversation

macurtis-amd
Copy link
Contributor

Fixes an issue introduced by

9fb2db1 [flang] Retain spaces when preprocessing fixed-form source

Where flang -fc1 fails to parse preprocessor output because it now remains in fixed form.

@llvmbot llvmbot added clang Clang issues not falling into any other category clang:driver 'clang' and 'clang++' user-facing binaries. Not 'clang-cl' flang:driver flang Flang issues not falling into any other category flang:parser labels Nov 25, 2024
@llvmbot
Copy link
Member

llvmbot commented Nov 25, 2024

@llvm/pr-subscribers-flang-parser
@llvm/pr-subscribers-flang-driver

@llvm/pr-subscribers-clang

Author: None (macurtis-amd)

Changes

Fixes an issue introduced by

9fb2db1 [flang] Retain spaces when preprocessing fixed-form source

Where flang -fc1 fails to parse preprocessor output because it now remains in fixed form.


Full diff: https://github.com/llvm/llvm-project/pull/117563.diff

6 Files Affected:

  • (modified) clang/include/clang/Driver/Types.def (+5-2)
  • (modified) clang/lib/Driver/ToolChain.cpp (+8-4)
  • (modified) clang/lib/Driver/Types.cpp (+8-6)
  • (modified) flang/lib/Frontend/CompilerInvocation.cpp (+12)
  • (added) flang/test/Driver/x-lang.f (+8)
  • (added) flang/test/Parser/x-f95-fixed.f (+17)
diff --git a/clang/include/clang/Driver/Types.def b/clang/include/clang/Driver/Types.def
index 214c5e7a789f97..5b64700c6a688b 100644
--- a/clang/include/clang/Driver/Types.def
+++ b/clang/include/clang/Driver/Types.def
@@ -88,8 +88,11 @@ TYPE("assembler-with-cpp",       Asm,          PP_Asm,          "S",      phases
 // modules when Flang needs to emit pre-processed files. Therefore, the
 // `PP_TYPE` is set to `PP_Fortran` so that the driver is fine with
 // "pre-processing a pre-processed file".
-TYPE("f95",                      PP_Fortran,   PP_Fortran,      "i",      phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link)
-TYPE("f95-cpp-input",            Fortran,      PP_Fortran,      nullptr,  phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link)
+TYPE("f95",                      PP_Fortran,       PP_Fortran,       "i",      phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link)
+TYPE("f95-cpp-input",            Fortran,          PP_Fortran,       nullptr,  phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link)
+TYPE("f95-fixed",                PP_Fortran_Fixed, PP_Fortran_Fixed, "i",      phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link)
+TYPE("f95-fixed-cpp-input",      Fortran_Fixed,    PP_Fortran_Fixed, nullptr,  phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link)
+
 TYPE("java",                     Java,         INVALID,         nullptr,  phases::Compile, phases::Backend, phases::Assemble, phases::Link)
 
 // LLVM IR/LTO types. We define separate types for IR and LTO because LTO
diff --git a/clang/lib/Driver/ToolChain.cpp b/clang/lib/Driver/ToolChain.cpp
index 0d426a467e9a3b..768e2e7842650c 100644
--- a/clang/lib/Driver/ToolChain.cpp
+++ b/clang/lib/Driver/ToolChain.cpp
@@ -1037,10 +1037,14 @@ types::ID ToolChain::LookupTypeForExtension(StringRef Ext) const {
   types::ID id = types::lookupTypeForExtension(Ext);
 
   // Flang always runs the preprocessor and has no notion of "preprocessed
-  // fortran". Here, TY_PP_Fortran is coerced to TY_Fortran to avoid treating
-  // them differently.
-  if (D.IsFlangMode() && id == types::TY_PP_Fortran)
-    id = types::TY_Fortran;
+  // fortran". Here, TY_PP_Fortran[_Fixed] is coerced to TY_Fortran[_Fixed] to
+  // avoid treating them differently.
+  if (D.IsFlangMode()) {
+    if (id == types::TY_PP_Fortran)
+      id = types::TY_Fortran;
+    else if (id == types::TY_PP_Fortran_Fixed)
+      id = types::TY_Fortran_Fixed;
+  }
 
   return id;
 }
diff --git a/clang/lib/Driver/Types.cpp b/clang/lib/Driver/Types.cpp
index eaca74a7b55292..19e2c55d4a663a 100644
--- a/clang/lib/Driver/Types.cpp
+++ b/clang/lib/Driver/Types.cpp
@@ -162,7 +162,9 @@ bool types::isAcceptedByFlang(ID Id) {
     return false;
 
   case TY_Fortran:
+  case TY_Fortran_Fixed:
   case TY_PP_Fortran:
+  case TY_PP_Fortran_Fixed:
     return true;
   case TY_LLVM_IR:
   case TY_LLVM_BC:
@@ -300,8 +302,8 @@ types::ID types::lookupTypeForExtension(llvm::StringRef Ext) {
   return llvm::StringSwitch<types::ID>(Ext)
       .Case("c", TY_C)
       .Case("C", TY_CXX)
-      .Case("F", TY_Fortran)
-      .Case("f", TY_PP_Fortran)
+      .Case("F", TY_Fortran_Fixed)
+      .Case("f", TY_PP_Fortran_Fixed)
       .Case("h", TY_CHeader)
       .Case("H", TY_CXXHeader)
       .Case("i", TY_PP_C)
@@ -344,10 +346,10 @@ types::ID types::lookupTypeForExtension(llvm::StringRef Ext) {
       .Case("f90", TY_PP_Fortran)
       .Case("F95", TY_Fortran)
       .Case("f95", TY_PP_Fortran)
-      .Case("for", TY_PP_Fortran)
-      .Case("FOR", TY_PP_Fortran)
-      .Case("fpp", TY_Fortran)
-      .Case("FPP", TY_Fortran)
+      .Case("for", TY_PP_Fortran_Fixed)
+      .Case("FOR", TY_PP_Fortran_Fixed)
+      .Case("fpp", TY_Fortran_Fixed)
+      .Case("FPP", TY_Fortran_Fixed)
       .Case("gch", TY_PCH)
       .Case("hip", TY_HIP)
       .Case("hipi", TY_PP_HIP)
diff --git a/flang/lib/Frontend/CompilerInvocation.cpp b/flang/lib/Frontend/CompilerInvocation.cpp
index 0b79c95eade0d3..acd884fb10b3fe 100644
--- a/flang/lib/Frontend/CompilerInvocation.cpp
+++ b/flang/lib/Frontend/CompilerInvocation.cpp
@@ -639,6 +639,7 @@ static bool parseFrontendArgs(FrontendOptions &opts, llvm::opt::ArgList &args,
 
   // Get the input kind (from the value passed via `-x`)
   InputKind dashX(Language::Unknown);
+  FortranForm dashXForm = FortranForm::Unknown;
   if (const llvm::opt::Arg *a =
           args.getLastArg(clang::driver::options::OPT_x)) {
     llvm::StringRef xValue = a->getValue();
@@ -648,6 +649,8 @@ static bool parseFrontendArgs(FrontendOptions &opts, llvm::opt::ArgList &args,
                 // pre-processed inputs.
                 .Case("f95", Language::Fortran)
                 .Case("f95-cpp-input", Language::Fortran)
+                .Case("f95-fixed", Language::Fortran)
+                .Case("f95-fixed-cpp-input", Language::Fortran)
                 // CUDA Fortran
                 .Case("cuda", Language::Fortran)
                 .Default(Language::Unknown);
@@ -663,6 +666,13 @@ static bool parseFrontendArgs(FrontendOptions &opts, llvm::opt::ArgList &args,
     if (dashX.isUnknown())
       diags.Report(clang::diag::err_drv_invalid_value)
           << a->getAsString(args) << a->getValue();
+
+    if (dashX.getLanguage() == Language::Fortran) {
+      if (xValue.starts_with("f95-fixed"))
+        dashXForm = FortranForm::FixedForm;
+      else
+        dashXForm = FortranForm::FreeForm;
+    }
   }
 
   // Collect the input files and save them in our instance of FrontendOptions.
@@ -694,6 +704,8 @@ static bool parseFrontendArgs(FrontendOptions &opts, llvm::opt::ArgList &args,
         arg->getOption().matches(clang::driver::options::OPT_ffixed_form)
             ? FortranForm::FixedForm
             : FortranForm::FreeForm;
+  } else if (dashXForm != FortranForm::Unknown) {
+    opts.fortranForm = dashXForm;
   }
 
   // Set fixedFormColumns based on -ffixed-line-length=<value>
diff --git a/flang/test/Driver/x-lang.f b/flang/test/Driver/x-lang.f
new file mode 100644
index 00000000000000..08282c9479f55a
--- /dev/null
+++ b/flang/test/Driver/x-lang.f
@@ -0,0 +1,8 @@
+!RUN: %flang -save-temps -### %S/Inputs/free-form-test.f90  2>&1 | FileCheck %s --check-prefix=FREE
+!RUN: %flang -save-temps -### %S/Inputs/fixed-form-test.f  2>&1 | FileCheck %s --check-prefix=FIXED
+
+FREE:       "-fc1" {{.*}} "-o" "free-form-test.i" {{.*}} "-x" "f95-cpp-input" "{{.*}}/free-form-test.f90"
+FREE-NEXT:  "-fc1" {{.*}} "-o" "free-form-test.bc" {{.*}} "-x" "f95" "free-form-test.i"
+
+FIXED:      "-fc1" {{.*}} "-o" "fixed-form-test.i" {{.*}} "-x" "f95-fixed-cpp-input" "{{.*}}/fixed-form-test.f"
+FIXED-NEXT: "-fc1" {{.*}} "-o" "fixed-form-test.bc" {{.*}} "-x" "f95-fixed" "fixed-form-test.i"
diff --git a/flang/test/Parser/x-f95-fixed.f b/flang/test/Parser/x-f95-fixed.f
new file mode 100644
index 00000000000000..2012bfc018ff12
--- /dev/null
+++ b/flang/test/Parser/x-f95-fixed.f
@@ -0,0 +1,17 @@
+      subroutine foo(a, b)
+      if ( (a  .eq. 0) .and.(b. eq. 1)) then
+         
+         print *, "foo"
+      end if
+      end subroutine
+
+! RUN: %flang_fc1 -fsyntax-only "-x" "f95-fixed" %s 2>&1 | FileCheck %s --allow-empty --check-prefix=F95-FIXED
+! F95-FIXED-NOT: Could not parse {{.*}}x-f95-fixed.f
+! F95-FIXED-NOT: error
+! F95-FIXED-NOT: warning
+
+! RUN: not %flang_fc1 -fsyntax-only "-x" "f95" %s 2>&1 | FileCheck %s --check-prefix=F95 --strict-whitespace
+! F95: error: Could not parse {{.*}}x-f95-fixed.f
+! F95: {{.*}}x-f95-fixed.f:2:31: error: expected ')'
+! F95:       if ( (a  .eq. 0) .and.(b. eq. 1)) then
+! F95: {{^([ ]{32})}}^

@llvmbot
Copy link
Member

llvmbot commented Nov 25, 2024

@llvm/pr-subscribers-clang-driver

Author: None (macurtis-amd)

Changes

Fixes an issue introduced by

9fb2db1 [flang] Retain spaces when preprocessing fixed-form source

Where flang -fc1 fails to parse preprocessor output because it now remains in fixed form.


Full diff: https://github.com/llvm/llvm-project/pull/117563.diff

6 Files Affected:

  • (modified) clang/include/clang/Driver/Types.def (+5-2)
  • (modified) clang/lib/Driver/ToolChain.cpp (+8-4)
  • (modified) clang/lib/Driver/Types.cpp (+8-6)
  • (modified) flang/lib/Frontend/CompilerInvocation.cpp (+12)
  • (added) flang/test/Driver/x-lang.f (+8)
  • (added) flang/test/Parser/x-f95-fixed.f (+17)
diff --git a/clang/include/clang/Driver/Types.def b/clang/include/clang/Driver/Types.def
index 214c5e7a789f97..5b64700c6a688b 100644
--- a/clang/include/clang/Driver/Types.def
+++ b/clang/include/clang/Driver/Types.def
@@ -88,8 +88,11 @@ TYPE("assembler-with-cpp",       Asm,          PP_Asm,          "S",      phases
 // modules when Flang needs to emit pre-processed files. Therefore, the
 // `PP_TYPE` is set to `PP_Fortran` so that the driver is fine with
 // "pre-processing a pre-processed file".
-TYPE("f95",                      PP_Fortran,   PP_Fortran,      "i",      phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link)
-TYPE("f95-cpp-input",            Fortran,      PP_Fortran,      nullptr,  phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link)
+TYPE("f95",                      PP_Fortran,       PP_Fortran,       "i",      phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link)
+TYPE("f95-cpp-input",            Fortran,          PP_Fortran,       nullptr,  phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link)
+TYPE("f95-fixed",                PP_Fortran_Fixed, PP_Fortran_Fixed, "i",      phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link)
+TYPE("f95-fixed-cpp-input",      Fortran_Fixed,    PP_Fortran_Fixed, nullptr,  phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link)
+
 TYPE("java",                     Java,         INVALID,         nullptr,  phases::Compile, phases::Backend, phases::Assemble, phases::Link)
 
 // LLVM IR/LTO types. We define separate types for IR and LTO because LTO
diff --git a/clang/lib/Driver/ToolChain.cpp b/clang/lib/Driver/ToolChain.cpp
index 0d426a467e9a3b..768e2e7842650c 100644
--- a/clang/lib/Driver/ToolChain.cpp
+++ b/clang/lib/Driver/ToolChain.cpp
@@ -1037,10 +1037,14 @@ types::ID ToolChain::LookupTypeForExtension(StringRef Ext) const {
   types::ID id = types::lookupTypeForExtension(Ext);
 
   // Flang always runs the preprocessor and has no notion of "preprocessed
-  // fortran". Here, TY_PP_Fortran is coerced to TY_Fortran to avoid treating
-  // them differently.
-  if (D.IsFlangMode() && id == types::TY_PP_Fortran)
-    id = types::TY_Fortran;
+  // fortran". Here, TY_PP_Fortran[_Fixed] is coerced to TY_Fortran[_Fixed] to
+  // avoid treating them differently.
+  if (D.IsFlangMode()) {
+    if (id == types::TY_PP_Fortran)
+      id = types::TY_Fortran;
+    else if (id == types::TY_PP_Fortran_Fixed)
+      id = types::TY_Fortran_Fixed;
+  }
 
   return id;
 }
diff --git a/clang/lib/Driver/Types.cpp b/clang/lib/Driver/Types.cpp
index eaca74a7b55292..19e2c55d4a663a 100644
--- a/clang/lib/Driver/Types.cpp
+++ b/clang/lib/Driver/Types.cpp
@@ -162,7 +162,9 @@ bool types::isAcceptedByFlang(ID Id) {
     return false;
 
   case TY_Fortran:
+  case TY_Fortran_Fixed:
   case TY_PP_Fortran:
+  case TY_PP_Fortran_Fixed:
     return true;
   case TY_LLVM_IR:
   case TY_LLVM_BC:
@@ -300,8 +302,8 @@ types::ID types::lookupTypeForExtension(llvm::StringRef Ext) {
   return llvm::StringSwitch<types::ID>(Ext)
       .Case("c", TY_C)
       .Case("C", TY_CXX)
-      .Case("F", TY_Fortran)
-      .Case("f", TY_PP_Fortran)
+      .Case("F", TY_Fortran_Fixed)
+      .Case("f", TY_PP_Fortran_Fixed)
       .Case("h", TY_CHeader)
       .Case("H", TY_CXXHeader)
       .Case("i", TY_PP_C)
@@ -344,10 +346,10 @@ types::ID types::lookupTypeForExtension(llvm::StringRef Ext) {
       .Case("f90", TY_PP_Fortran)
       .Case("F95", TY_Fortran)
       .Case("f95", TY_PP_Fortran)
-      .Case("for", TY_PP_Fortran)
-      .Case("FOR", TY_PP_Fortran)
-      .Case("fpp", TY_Fortran)
-      .Case("FPP", TY_Fortran)
+      .Case("for", TY_PP_Fortran_Fixed)
+      .Case("FOR", TY_PP_Fortran_Fixed)
+      .Case("fpp", TY_Fortran_Fixed)
+      .Case("FPP", TY_Fortran_Fixed)
       .Case("gch", TY_PCH)
       .Case("hip", TY_HIP)
       .Case("hipi", TY_PP_HIP)
diff --git a/flang/lib/Frontend/CompilerInvocation.cpp b/flang/lib/Frontend/CompilerInvocation.cpp
index 0b79c95eade0d3..acd884fb10b3fe 100644
--- a/flang/lib/Frontend/CompilerInvocation.cpp
+++ b/flang/lib/Frontend/CompilerInvocation.cpp
@@ -639,6 +639,7 @@ static bool parseFrontendArgs(FrontendOptions &opts, llvm::opt::ArgList &args,
 
   // Get the input kind (from the value passed via `-x`)
   InputKind dashX(Language::Unknown);
+  FortranForm dashXForm = FortranForm::Unknown;
   if (const llvm::opt::Arg *a =
           args.getLastArg(clang::driver::options::OPT_x)) {
     llvm::StringRef xValue = a->getValue();
@@ -648,6 +649,8 @@ static bool parseFrontendArgs(FrontendOptions &opts, llvm::opt::ArgList &args,
                 // pre-processed inputs.
                 .Case("f95", Language::Fortran)
                 .Case("f95-cpp-input", Language::Fortran)
+                .Case("f95-fixed", Language::Fortran)
+                .Case("f95-fixed-cpp-input", Language::Fortran)
                 // CUDA Fortran
                 .Case("cuda", Language::Fortran)
                 .Default(Language::Unknown);
@@ -663,6 +666,13 @@ static bool parseFrontendArgs(FrontendOptions &opts, llvm::opt::ArgList &args,
     if (dashX.isUnknown())
       diags.Report(clang::diag::err_drv_invalid_value)
           << a->getAsString(args) << a->getValue();
+
+    if (dashX.getLanguage() == Language::Fortran) {
+      if (xValue.starts_with("f95-fixed"))
+        dashXForm = FortranForm::FixedForm;
+      else
+        dashXForm = FortranForm::FreeForm;
+    }
   }
 
   // Collect the input files and save them in our instance of FrontendOptions.
@@ -694,6 +704,8 @@ static bool parseFrontendArgs(FrontendOptions &opts, llvm::opt::ArgList &args,
         arg->getOption().matches(clang::driver::options::OPT_ffixed_form)
             ? FortranForm::FixedForm
             : FortranForm::FreeForm;
+  } else if (dashXForm != FortranForm::Unknown) {
+    opts.fortranForm = dashXForm;
   }
 
   // Set fixedFormColumns based on -ffixed-line-length=<value>
diff --git a/flang/test/Driver/x-lang.f b/flang/test/Driver/x-lang.f
new file mode 100644
index 00000000000000..08282c9479f55a
--- /dev/null
+++ b/flang/test/Driver/x-lang.f
@@ -0,0 +1,8 @@
+!RUN: %flang -save-temps -### %S/Inputs/free-form-test.f90  2>&1 | FileCheck %s --check-prefix=FREE
+!RUN: %flang -save-temps -### %S/Inputs/fixed-form-test.f  2>&1 | FileCheck %s --check-prefix=FIXED
+
+FREE:       "-fc1" {{.*}} "-o" "free-form-test.i" {{.*}} "-x" "f95-cpp-input" "{{.*}}/free-form-test.f90"
+FREE-NEXT:  "-fc1" {{.*}} "-o" "free-form-test.bc" {{.*}} "-x" "f95" "free-form-test.i"
+
+FIXED:      "-fc1" {{.*}} "-o" "fixed-form-test.i" {{.*}} "-x" "f95-fixed-cpp-input" "{{.*}}/fixed-form-test.f"
+FIXED-NEXT: "-fc1" {{.*}} "-o" "fixed-form-test.bc" {{.*}} "-x" "f95-fixed" "fixed-form-test.i"
diff --git a/flang/test/Parser/x-f95-fixed.f b/flang/test/Parser/x-f95-fixed.f
new file mode 100644
index 00000000000000..2012bfc018ff12
--- /dev/null
+++ b/flang/test/Parser/x-f95-fixed.f
@@ -0,0 +1,17 @@
+      subroutine foo(a, b)
+      if ( (a  .eq. 0) .and.(b. eq. 1)) then
+         
+         print *, "foo"
+      end if
+      end subroutine
+
+! RUN: %flang_fc1 -fsyntax-only "-x" "f95-fixed" %s 2>&1 | FileCheck %s --allow-empty --check-prefix=F95-FIXED
+! F95-FIXED-NOT: Could not parse {{.*}}x-f95-fixed.f
+! F95-FIXED-NOT: error
+! F95-FIXED-NOT: warning
+
+! RUN: not %flang_fc1 -fsyntax-only "-x" "f95" %s 2>&1 | FileCheck %s --check-prefix=F95 --strict-whitespace
+! F95: error: Could not parse {{.*}}x-f95-fixed.f
+! F95: {{.*}}x-f95-fixed.f:2:31: error: expected ')'
+! F95:       if ( (a  .eq. 0) .and.(b. eq. 1)) then
+! F95: {{^([ ]{32})}}^

@banach-space
Copy link
Contributor

Adding new types to Types.def is a fairly huge design update. I'm not saying this is approach is incorrect, but it definitely deserves a bit of discussion. Perhaps under a GitHub issue? I don't really mind.

Could you quickly summarise what's not working and why adding new types to Types.def solves that? Is that the only solution?

@DavidTruby
Copy link
Member

Thanks for the fix, I'd like to understand what the bug is better though; what is the difference between the proposed flang -fc1 -x f95-fixed and the existing flang -fc1 -ffixed-form?
In the test case in here I already don't see an error with flang -fc1 -ffixed-form, is there an example you have where this fails to compile but -x f95-fixed works?

@macurtis-amd
Copy link
Contributor Author

Thanks @banach-space and @DavidTruby for looking at this.

banach-space: Adding new types to Types.def is a fairly huge design update. I'm not saying this is approach is incorrect, but it definitely deserves a bit of discussion. Perhaps under a GitHub issue? I don't really mind.

Created issue 117712. We can continue discussion there if that is the right place.

banach-space: Could you quickly summarise what's not working

From that issue:

Here is the problematic fixed form fortran source reduced.f:

      subroutine foo(a, b)
      if ( (a  .eq. 0) .and.
     >     (b . eq. 1) ) then
         print *, "foo"
      end if
      end subroutine

Prior to 9fb2db1 flang -save-temps -c produced reduced.i:

      subroutine foo(a, b)
      if ( (a  .eq. 0) .and.(b.eq.1))then

         print *, "foo"
      end if
      end subroutine

which compiles without error.

With 9fb2db1, flang -save-temps -c produces reduced.i:

      subroutine foo(a, b)
      if ( (a  .eq. 0) .and.(b. eq. 1)) then

         print *, "foo"
      end if
      end subroutine

Which produces:

error: Could not parse reduced.i
./reduced.f:2:31: error: expected ')'
        if ( (a  .eq. 0) .and.(b. eq. 1)) then
                                ^
...

In either case the commands produced by the driver look like:

flang-new -fc1 -E -o reduced.i -x f95-cpp-input reduced.f
flangnew -fc1 -emit-llvm-bc ... -o reduced.bc -x f95 reduced.i
...

banach-space: ... and why adding new types to Types.def solves that?

With this change, the driver produces:

flang-new -fc1 -E ... -o reduced.i -x f95-fixed-cpp-input reduced.f
flang-new -fc1 -emit-llvm-bc -o reduced.bc -x f95-fixed reduced.i

Which preserves the fact that reduced.i contains fixed form fortran.

banach-space: Is that the only solution?

No. I initially looked at modifying Flang::ConstructJob to add -ffixed-form to the compilation command. But I found myself having to walk back through the compilation graph to get the original filename extension so I could determine if it was fixed or free.

It worked, but seemed brittle. It does have the benefit of being more surgical.

Another solution I considered, was propagating the fixed vs free information in some way other than the type. I didn't go down this path because it seemed more natural to consider it part of the type.

FWIW, I don't have strong feelings here. I'm happy to implement the fix however you all think is best.

DavidTruby: what is the difference between the proposed flang -fc1 -x f95-fixed and the existing flang -fc1 -ffixed-form?

DavidTruby: In the test case in here I already don't see an error with flang -fc1 -ffixed-form, is there an example you have where this fails to compile but -x f95-fixed works?

Sorry. I should have put this in the commit description, but the issue is specific to the use of -save-temps. If manually invoking flang -fc1 there is no benefit to -x f95-fixed. The problem is the driver needs to know to add -ffixed-form when building the compilation command. As noted above, I tried doing this, but was not happy with the resulting code.

@tarunprabhu
Copy link
Contributor

      if ( (a  .eq. 0) .and.(b. eq. 1)) then

It seems like something in 9fb2db1 has caused a space to appear between . and eq. Should we be looking at what that commit did more closely or am I missing something obvious? (alas, it wouldn't surprise me if I was)

@DavidTruby
Copy link
Member

DavidTruby commented Nov 27, 2024

Ah I see, I can reproduce your issue in the presence of -save-temps.

However, -save-temps doesn't appear to work for me at all. I get the following error on a trivial file (containing just end program):

 "S:\\llvm-project\\build\\bin\\flang.exe" -cc1as -triple x86_64-pc-windows-msvc19.41.34123 -filetype obj -main-file-name test.f90 -target-cpu x86-64 "-fdebug-compilation-dir=S:\\llvm-project\\build" -dwarf-version=4 -mrelocation-model pic -mincremental-linker-compatible -o test.o test.s
error: unknown integrated tool '-cc1as'. Valid tools include '-fc1'.

Is it something we are expecting to work with the flang compiler driver? I suspected this might be a Windows issue but I can reproduce this on godbolt.org. If -save-temps doesn't work anyway even in simple cases, should we just disable it?

Anyways, I think to fix the specific issue you're reporting here, an easier method is rely on the fact that the "preprocessed" output from flang will always be fixed form, regardless of what form the input was in. So instead of modifying Types.def you can add ".i" to the suffixes where fixed-format is assumed here: https://github.com/llvm/llvm-project/blob/main/flang/lib/Frontend/FrontendOptions.cpp#L21-L22. This allows me to get past the error you're seeing (and back to the cc1as error).

It feels wrong that -save-temps runs the preprocessor separately anyway for flang. There's no "temp" generated by the flang preprocessor as preprocessing is not a separate step to parsing. We don't appear to need to do this for compatibility with gfortran either because gfortran doesn't appear to save a preprocessed file with -save-temps (at least on my system). Even if we do chose to have -save-temps save a preprocessed file for some reason, I think .i is the wrong extension as that's usually used for C/C++ preprocessed files.
Does your use case specifically require -save-temps to save a preprocessed file, and if so does it have to have the .i extension? If not, I'd propose we modify -save-temps to not preprocess separately, but my suggestion above (treating .i as fixed format input in the flang frontend driver (flang -fc1)) would be fine as a temporary fix.

@banach-space
Copy link
Contributor

banach-space commented Nov 27, 2024

However, -save-temps doesn't appear to work for me at all.

You need to add -fno-integrated-as. This is a known limitation:

It feels wrong that -save-temps runs the preprocessor separately anyway for flang. There's no "temp" generated by the flang preprocessor as preprocessing is not a separate step to parsing.

-save-temps "approximates" the actual compilation flow. This behavior is consistent with Clang, where preprocessing, assembling, etc., are split into separate steps. The need for -fno-integrated-as is a side effect of this: -save-temps modifies the invocation to run the assembler as a standalone step, but Flang doesn't yet support the "integrated" assembler that Clang uses by default.

We don't appear to need to do this for compatibility with gfortran either because gfortran doesn't appear to save a preprocessed file with -save-temps (at least on my system).

That's correct. However, LLVM Flang generally follows Clang in the absence of explicit direction from GFortran. This ensures consistent behavior between clang and flang, which is the case here.

Even if we do chose to have -save-temps save a preprocessed file for some reason, I think .i is the wrong extension as that's usually used for C/C++ preprocessed files.

Agreed, .i is typically associated with C/C++. However, Flang uses .i for historical reasons. If .i isn't ideal, what alternative would you suggest for Fortran preprocessed files?

banach-space: Is that the only solution?

No. I initially looked at modifying Flang::ConstructJob to add -ffixed-form to the compilation command. But I found myself having to walk back through the compilation graph to get the original filename extension so I could determine if it was fixed or free.

It worked, but seemed brittle. It does have the benefit of being more surgical.

Another solution I considered, was propagating the fixed vs free information in some way other than the type. I didn't go down this path because it seemed more natural to consider it part of the type.

FWIW, I don't have strong feelings here. I'm happy to implement the fix however you all think is best.

@macurtis-amd, thanks for the detailed explanation. The approach you're suggesting seems the most natural, provided we can guarantee or agree on the following:

  • Fixed-form → flang -E → fixed-form.
  • Free-form → flang -E → free-form.

Alternatively, we could simplify by requiring that flang -E always produces free-form output. Would that be acceptable to everyone?

@DavidTruby
Copy link
Member

DavidTruby commented Nov 27, 2024

Thanks for the info on -save-temps, I understand it a little better now !

we could simplify by requiring that flang -E always produces free-form output.

flang -E is already consistent here, it always produces fixed form output regardless of input form. So I think we can just teach flang -fc1 that '.i' files are fixed form. Or just pick another file extension to use ('.fpp'?) and teach it that that is fixed form? I suppose using '.i' doesn't really matter since these files aren't passed to a driver that needs to determine if the file is C or Fortran.

If the frontend driver is told that .i files are fixed form then both the examples above and free form examples work for me with -save-temps -fno-integrated-as

@banach-space
Copy link
Contributor

flang -E is already consistent here, it always produces fixed form output.

It would be good to document and to test that - perhaps that's already the case?

So I think we can just teach flang -fc1 that '.i'

Agreed, and this should be trivial to implement. Thanks @DavidTruby !

@DavidTruby
Copy link
Member

I'm not sure if it's explicitly documented anywhere but there's a lot of tests, as you can see in the patch that introduced the behaviour here: https://reviews.llvm.org/D106727

Essentially since that patch the output is always valid 72 width fixed form, and if the input was free form it'll also be valid free form (but still valid fixed form too! It's possible to write in a style that can be compiled as both)

@macurtis-amd
Copy link
Contributor Author

(Sorry for the delayed response ... was on Thanksgiving holiday)

@banach-space @DavidTruby Thanks for looking at this!

Latest version of the fix simply adds -ffixed-form for pre-processed input, unless the user manually specifies -ffixed-form or -ffree-form.

@macurtis-amd macurtis-amd changed the title [flang] Preserve fixed form in fc1 -x value [flang] Treat pre-processed input as fixed Dec 2, 2024
Comment on lines 784 to 785
!Args.hasArg(options::OPT_ffixed_form) &&
!Args.hasArg(options::OPT_ffree_form)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should capture the right-most instance of -ffixed-form/-ffree-form, which is exactly what you want here:

            Args.getLastArg(clang::driver::options::OPT_ffixed_form,
                            clang::driver::options::OPT_ffree_form))

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated to use getLastArg

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not seeing getLastArg here on the github review.. Is this github being weird?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not seeing getLastArg here on the github review.. Is this github being weird?

Oops. Really updated to use getLastArg now.

Copy link
Member

@DavidTruby DavidTruby left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks for being patient with our requests for changes!

Copy link
Contributor

@banach-space banach-space left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thank you for all the detective work!

@macurtis-amd macurtis-amd merged commit 81d82ca into llvm:main Dec 3, 2024
8 checks passed
@macurtis-amd
Copy link
Contributor Author

LGTM, thanks for being patient with our requests for changes!

LGTM, thank you for all the detective work!

Happy to help!

inaki-amatria added a commit to inaki-amatria/llvm-project that referenced this pull request Feb 20, 2025
inaki-amatria added a commit to inaki-amatria/llvm-project that referenced this pull request Mar 4, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:driver 'clang' and 'clang++' user-facing binaries. Not 'clang-cl' clang Clang issues not falling into any other category flang:driver flang:parser flang Flang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants