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
