[flang-commits] [flang] 22e13e7 - [Flang] Fix device-side module lookup (#200863)
via flang-commits
flang-commits at lists.llvm.org
Mon Jun 1 16:48:00 PDT 2026
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
More information about the flang-commits
mailing list