Author: Michael Kruse
Date: 2026-06-02T01:47:56+02:00
New Revision: 22e13e71ed46f0d47f90ae5e6cea2956db892be3

URL: 
https://github.com/llvm/llvm-project/commit/22e13e71ed46f0d47f90ae5e6cea2956db892be3
DIFF: 
https://github.com/llvm/llvm-project/commit/22e13e71ed46f0d47f90ae5e6cea2956db892be3.diff

LOG: [Flang] Fix device-side module lookup (#200863)

When invoking flang with device-offloading (eg. `flang modfile.f90
-fopenmp --offload-arch=gfx90a`), it will invoke the frontend twice:
once for the host architecture, and a second time for the architecture
specified with `--offload-arch`. However, both frontend invocations are
going to write `modfile.mod` (or whatever the module name in
`modfile.f90`), and as a result the second one for gfx90a will be what
the file contains after the driver invocation returns. Until #171515
both version of the file were identical, but now both files are using a
different set of builtin modules. Since Flang's mod files store the
checksums of used module files in them, this can result in a checksum
mismatch error. For instance, modfile.mod being the gfx90a version, and
then using it to compile with `flang modfile.f90
--target=x86_64-linux-gnu`) will have a checksum mismath.

flang -fc1 host x86_64        --> modfile.mod --> 
lib/clang/23/finclude/flang/x86_64-linux-gnu/iso_fortran_env.mod
                                 /  /     \  \
flang -fc1 -foffload-device nvptx  /       \  
lib/clang/23/finclude/flang/nvptx64-nvidia-cuda/iso_fortran_env.mod
                                  /         \
flang -fc1 -foffload-device amdgcn           
lib/clang/23/finclude/flang/amdgcn-amd-amdhsa/iso_fortran_env.mod

We fix this by

1. Not overwriting the `--target` host module file with the
   `--offload-arch` module; the auxiliary target is the canonical version
   for its contents; and

2. Ignore checksum errors when using an intrinsic module during
   offloading. The device version should be compatible with the host
   version, just with definitions which the .mod file will eventually
   import from the intrinsic module at compile-time.

Added: 
    flang/test/Semantics/Inputs/device_modfile01_a.mod
    flang/test/Semantics/device-modfile01.f90
    flang/test/Semantics/device-modfile02.f90

Modified: 
    clang/include/clang/Options/FlangOptions.td
    clang/lib/Driver/ToolChains/Flang.cpp
    flang/include/flang/Support/LangOptions.def
    flang/lib/Frontend/CompilerInvocation.cpp
    flang/lib/Semantics/mod-file.cpp
    flang/lib/Semantics/semantics.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/Options/FlangOptions.td 
b/clang/include/clang/Options/FlangOptions.td
index 4fa0d7a9abcc6..41d908b3eb43e 100644
--- a/clang/include/clang/Options/FlangOptions.td
+++ b/clang/include/clang/Options/FlangOptions.td
@@ -392,6 +392,7 @@ def fno_reformat : Flag<["-"], "fno-reformat">, 
Group<Preprocessor_Group>,
 def fpreprocess_include_lines : Flag<["-"], "fpreprocess-include-lines">, 
Group<Preprocessor_Group>,
   HelpText<"Treat INCLUDE lines like #include directives in -E mode">;
 defm analyzed_objects_for_unparse : 
OptOutFC1FFlag<"analyzed-objects-for-unparse", "", "Do not use the analyzed 
objects when unparsing">;
+def foffload_device : Flag<["-"], "foffload-device">, Group<f_Group>, 
Flags<[HelpHidden]>;
 
 def emit_fir : Flag<["-"], "emit-fir">, Group<Action_Group>,
   HelpText<"Build the parse tree, then lower it to FIR">;

diff  --git a/clang/lib/Driver/ToolChains/Flang.cpp 
b/clang/lib/Driver/ToolChains/Flang.cpp
index 892a455167205..ae9ae8176e281 100644
--- a/clang/lib/Driver/ToolChains/Flang.cpp
+++ b/clang/lib/Driver/ToolChains/Flang.cpp
@@ -690,6 +690,11 @@ void Flang::addOffloadOptions(Compilation &C, const 
InputInfoList &Inputs,
   bool IsHostOffloadingAction = JA.isHostOffloading(Action::OFK_OpenMP) ||
                                 JA.isHostOffloading(C.getActiveOffloadKinds());
 
+  // Tell the frontend when it is compiling for an offloading device, 
regardless
+  // of offloading programming model.
+  if (IsHostOffloadingAction)
+    CmdArgs.push_back("-offload-device");
+
   // Skips the primary input file, which is the input file that the compilation
   // proccess will be executed upon (e.g. the host bitcode file) and
   // adds other secondary input (e.g. device bitcode files for embedding to the

diff  --git a/flang/include/flang/Support/LangOptions.def 
b/flang/include/flang/Support/LangOptions.def
index c2d6f80132cfe..7ae73c6755b57 100644
--- a/flang/include/flang/Support/LangOptions.def
+++ b/flang/include/flang/Support/LangOptions.def
@@ -42,6 +42,9 @@ LANGOPT(ReciprocalMath, 1, false)
 LANGOPT(OpenMPVersion, 31, 0)
 /// Generate code only for OpenMP target device
 LANGOPT(OpenMPIsTargetDevice, 1, false)
+/// Indicate we are compiling offloading device-side code, not for the
+/// host/auxiliary device.
+LANGOPT(OffloadDevice, 1, false)
 /// Generate OpenMP target code only for GPUs
 LANGOPT(OpenMPIsGPU, 1, false)
 /// Generate OpenMP target code only for GPUs

diff  --git a/flang/lib/Frontend/CompilerInvocation.cpp 
b/flang/lib/Frontend/CompilerInvocation.cpp
index 9853fc600ff6a..7349b60c3caa1 100644
--- a/flang/lib/Frontend/CompilerInvocation.cpp
+++ b/flang/lib/Frontend/CompilerInvocation.cpp
@@ -1728,6 +1728,9 @@ bool CompilerInvocation::createFromArgs(
   invoc.frontendOpts.llvmArgs = 
args.getAllArgValues(clang::options::OPT_mllvm);
   invoc.frontendOpts.mlirArgs = 
args.getAllArgValues(clang::options::OPT_mmlir);
 
+  if (args.hasArg(clang::options::OPT_foffload_device))
+    invoc.getLangOpts().OffloadDevice = 1;
+
   success &= parseLangOptionsArgs(invoc, args, diags);
 
   success &= parseLinkerOptionsArgs(invoc, args, diags);

diff  --git a/flang/lib/Semantics/mod-file.cpp 
b/flang/lib/Semantics/mod-file.cpp
index f5e66a04c3f11..27e69f5271b55 100644
--- a/flang/lib/Semantics/mod-file.cpp
+++ b/flang/lib/Semantics/mod-file.cpp
@@ -1499,8 +1499,19 @@ Scope *ModFileReader::Read(SourceName name, 
std::optional<bool> isIntrinsic,
     }
     ancestorName = ancestor->GetName().value().ToString();
   }
-  auto requiredHash{context_.moduleDependences().GetRequiredHash(
-      name.ToString(), isIntrinsic.value_or(false))};
+
+  // When offloading modules files are created for the host, but when compiling
+  // device-side code the builtin modules are exchanged with device-specific
+  // versions. They contain matching declarations, but have 
diff erent checksums.
+  bool ignoreChecksumMismatch{
+      context_.langOptions().OffloadDevice && isIntrinsic.value_or(false)};
+
+  std::optional<size_t> requiredHash;
+  if (!ignoreChecksumMismatch) {
+    requiredHash = context_.moduleDependences().GetRequiredHash(
+        name.ToString(), isIntrinsic.value_or(false));
+  }
+
   if (!isIntrinsic.value_or(false) && !ancestor) {
     // Already present in the symbol table as a usable non-intrinsic module?
     if (Scope * hermeticScope{context_.currentHermeticModuleFileScope()}) {
@@ -1584,7 +1595,7 @@ Scope *ModFileReader::Read(SourceName name, 
std::optional<bool> isIntrinsic,
     for (const auto &dir : context_.intrinsicModuleDirectories()) {
       options.searchDirectories.push_back(dir);
     }
-    if (!requiredHash) {
+    if (!requiredHash && !ignoreChecksumMismatch) {
       requiredHash =
           context_.moduleDependences().GetRequiredHash(name.ToString(), true);
     }

diff  --git a/flang/lib/Semantics/semantics.cpp 
b/flang/lib/Semantics/semantics.cpp
index f852c9e59d419..e4ac2f73c3976 100644
--- a/flang/lib/Semantics/semantics.cpp
+++ b/flang/lib/Semantics/semantics.cpp
@@ -699,15 +699,24 @@ bool Semantics::Perform() {
       }
     }
   }
-  return ValidateLabels(context_, program_) &&
-      parser::CanonicalizeDo(program_) && // force line break
-      CanonicalizeAcc(context_.messages(), program_) &&
-      CanonicalizeOmp(context_, program_) && CanonicalizeCUDA(program_) &&
-      PerformStatementSemantics(context_, program_) &&
-      CanonicalizeDirectives(context_.messages(), program_) &&
-      ModFileWriter{context_}
-          .set_hermeticModuleFileOutput(hermeticModuleFileOutput_)
-          .WriteAll();
+  if (!(ValidateLabels(context_, program_) &&
+          parser::CanonicalizeDo(program_) && // force line break
+          CanonicalizeAcc(context_.messages(), program_) &&
+          CanonicalizeOmp(context_, program_) && CanonicalizeCUDA(program_) &&
+          PerformStatementSemantics(context_, program_) &&
+          CanonicalizeDirectives(context_.messages(), program_))) {
+    return false;
+  }
+
+  // When compiling with offloading, write only the host's module file. The
+  // device invocations would otherwise overwrite the host's mod file.
+  if (context_.langOptions().OffloadDevice) {
+    return true;
+  }
+
+  return ModFileWriter{context_}
+      .set_hermeticModuleFileOutput(hermeticModuleFileOutput_)
+      .WriteAll();
 }
 
 void Semantics::EmitMessages(llvm::raw_ostream &os) {

diff  --git a/flang/test/Semantics/Inputs/device_modfile01_a.mod 
b/flang/test/Semantics/Inputs/device_modfile01_a.mod
new file mode 100644
index 0000000000000..ccc982ef4f24b
--- /dev/null
+++ b/flang/test/Semantics/Inputs/device_modfile01_a.mod
@@ -0,0 +1,6 @@
+!mod$ v1 sum:bee3e43d23f6ccad
+!need$ 0000000000000000 i iso_fortran_env
+module device_modfile01_a
+use,intrinsic::iso_fortran_env,only:int32
+integer(4)::x
+end

diff  --git a/flang/test/Semantics/device-modfile01.f90 
b/flang/test/Semantics/device-modfile01.f90
new file mode 100644
index 0000000000000..fcd0b50750667
--- /dev/null
+++ b/flang/test/Semantics/device-modfile01.f90
@@ -0,0 +1,14 @@
+! Inputs/device-side-modules-a.mod records a deliberately wrong checksum for 
its
+! dependency on the intrinsic module iso_fortran_env.
+
+! Without offloading the recorded checksum is enforced and the wrong-checksum
+! dependency is rejected.
+! RUN: not %flang_fc1 -fsyntax-only -I%S/Inputs %s 2>&1 | FileCheck %s 
--check-prefix=HOST
+! HOST: error: Cannot use module file for module 'iso_fortran_env': File is 
not the right module file for 'iso_fortran_env'
+
+! When compiling for the device, the intrinsic module checksum is ignored.
+! RUN: %flang_fc1 -fsyntax-only -foffload-device -I%S/Inputs %s
+
+module device_modfile01
+  use device_modfile01_a, only: x
+end module

diff  --git a/flang/test/Semantics/device-modfile02.f90 
b/flang/test/Semantics/device-modfile02.f90
new file mode 100644
index 0000000000000..4feb38e304f9e
--- /dev/null
+++ b/flang/test/Semantics/device-modfile02.f90
@@ -0,0 +1,7 @@
+! The device side must not write module files (they belong to the host).
+! RUN: rm -rf %t && mkdir -p %t
+! RUN: %flang_fc1 -fsyntax-only -foffload-device -I%S/Inputs -J%t %s
+! RUN: not ls %t/device_modfile02.mod
+
+module device_modfile02
+end module


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to