[flang][Semantics] Ensure deterministic mod file output (#129669)

This PR is a follow-up to #128655.

It adds another test to ensure deterministic ordering in `.mod` files
and includes related changes to prevent non-deterministic ordering
caused by iterating over a set ordered by heap pointers. This issue is
particularly noticeable when using Flang as a library and compiling the
same files multiple times.

The reduced test case is as minimal as possible. We were unable to
reproduce the issue with a smaller set of files.
This commit is contained in:
Iñaki Amatria Barral 2025-03-05 08:27:17 +01:00 committed by GitHub
parent 107fe0ec6c
commit 6eefadd8ef
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 346 additions and 3 deletions

View File

@ -47,7 +47,7 @@ struct ModHeader {
static std::optional<SourceName> GetSubmoduleParent(const parser::Program &);
static void CollectSymbols(
const Scope &, SymbolVector &, SymbolVector &, UnorderedSymbolSet &);
const Scope &, SymbolVector &, SymbolVector &, SourceOrderedSymbolSet &);
static void PutPassName(llvm::raw_ostream &, const std::optional<SourceName> &);
static void PutInit(llvm::raw_ostream &, const Symbol &, const MaybeExpr &,
const parser::Expr *);
@ -363,7 +363,7 @@ void ModFileWriter::PutSymbols(
auto &renamings{context_.moduleFileOutputRenamings()};
auto previousRenamings{std::move(renamings)};
PrepareRenamings(scope);
UnorderedSymbolSet modules;
SourceOrderedSymbolSet modules;
CollectSymbols(scope, sorted, uses, modules);
// Write module files for dependencies first so that their
// hashes are known.
@ -839,7 +839,7 @@ void ModFileWriter::PutUseExtraAttr(
// Collect the symbols of this scope sorted by their original order, not name.
// Generics and namelists are exceptions: they are sorted after other symbols.
void CollectSymbols(const Scope &scope, SymbolVector &sorted,
SymbolVector &uses, UnorderedSymbolSet &modules) {
SymbolVector &uses, SourceOrderedSymbolSet &modules) {
SymbolVector namelist, generics;
auto symbols{scope.GetSymbols()};
std::size_t commonSize{scope.commonBlocks().size()};

View File

@ -0,0 +1,197 @@
MODULE modfile73a
PUBLIC re_alloc, defaults
integersp
integerselected_real_kind0
integer:: i8b = selected_int_kind(8)
interface
subroutine alloc_error_report_interf(str,code)
end
subroutine alloc_memory_event_interf(bytes,name)
end
end interface
procedure()alloc_error_report
procedure()alloc_memory_event
interface de_alloc
end interface
charactercharacter, DEFAULT_ROUTINE
type allocDefaults
logical copy
logical shrink
integer imin
characterroutine
end type
type(allocDefaults)DEFAULT
integer IERR
logical ASSOCIATED_ARRAY, NEEDS_ALLOC, NEEDS_COPY, NEEDS_DEALLOC
CONTAINS
subroutine set_alloc_event_handler(func)
end
subroutine set_alloc_error_handler(func)
end
subroutine dummy_alloc_memory_event(bytes,name)
end
subroutine dummy_alloc_error_report(name,code)
end
SUBROUTINE alloc_default( old, new, restore, routine, copy, shrink, imin )
END
SUBROUTINE realloc_i1( array, i1min, i1max, name, routine, copy, shrink )
END
SUBROUTINE realloc_i2( array, i1min,i1max, i2min,i2max, name, routine, copy, shrink )
END
SUBROUTINE realloc_i3( array, i1min,i1max, i2min,i2max, i3min,i3max, name, routine, copy, shrink )
END
SUBROUTINE realloc_i4( array, i1min,i1max, i2min,i2max, i3min,i3max, i4min,i4max, name, routine, copy, shrink )
END
SUBROUTINE realloc_i5( array, i1min,i1max, i2min,i2max, i3min,i3max, i4min, i4max, i5min, i5max, name, routine, copy, shrink )
END
SUBROUTINE realloc_E1( array, i1min, i1max, name, routine, copy, shrink )
END
SUBROUTINE realloc_r1( array, i1min, i1max, name, routine, copy, shrink )
END
SUBROUTINE realloc_r2( array, i1min,i1max, i2min,i2max, name, routine, copy, shrink )
END
SUBROUTINE realloc_r3( array, i1min,i1max, i2min,i2max, i3min,i3max, name, routine, copy, shrink )
END
SUBROUTINE realloc_r4( array, i1min,i1max, i2min,i2max, i3min,i3max, i4min,i4max, name, routine, copy, shrink )
END
SUBROUTINE realloc_r5( array, i1min,i1max, i2min,i2max, i3min,i3max, i4min, i4max, i5min, i5max, name, routine, copy, shrink )
END
SUBROUTINE realloc_d1( array, i1min, i1max, name, routine, copy, shrink )
END
SUBROUTINE realloc_d2( array, i1min,i1max, i2min,i2max, name, routine, copy, shrink )
END
SUBROUTINE realloc_d3( array, i1min,i1max, i2min,i2max, i3min,i3max, name, routine, copy, shrink )
END
SUBROUTINE realloc_d4( array, i1min,i1max, i2min,i2max, i3min,i3max, i4min,i4max, name, routine, copy, shrink )
END
SUBROUTINE realloc_d5( array, i1min,i1max, i2min,i2max, i3min,i3max, i4min,i4max, i5min,i5max, name, routine, copy, shrink )
END
SUBROUTINE realloc_c1( array, i1min, i1max, name, routine, copy, shrink )
END
SUBROUTINE realloc_c2( array, i1min,i1max, i2min,i2max, name, routine, copy, shrink )
END
SUBROUTINE realloc_c3( array, i1min,i1max, i2min,i2max, i3min,i3max, name, routine, copy, shrink )
END
SUBROUTINE realloc_c4( array, i1min,i1max, i2min,i2max, i3min,i3max, i4min,i4max, name, routine, copy, shrink )
END
SUBROUTINE realloc_c5( array, i1min,i1max, i2min,i2max, i3min,i3max, i4min, i4max, i5min, i5max, name, routine, copy, shrink )
END
SUBROUTINE realloc_z1( array, i1min, i1max, name, routine, copy, shrink )
END
SUBROUTINE realloc_z2( array, i1min,i1max, i2min,i2max, name, routine, copy, shrink )
END
SUBROUTINE realloc_z3( array, i1min,i1max, i2min,i2max, i3min,i3max, name, routine, copy, shrink )
END
SUBROUTINE realloc_z4( array, i1min,i1max, i2min,i2max, i3min,i3max, i4min,i4max, name, routine, copy, shrink )
END
SUBROUTINE realloc_z5( array, i1min,i1max, i2min,i2max, i3min,i3max, i4min, i4max, i5min, i5max, name, routine, copy, shrink )
END
SUBROUTINE realloc_l1( array, i1min,i1max, name, routine, copy, shrink )
END
SUBROUTINE realloc_l2( array, i1min,i1max, i2min,i2max, name, routine, copy, shrink )
END
SUBROUTINE realloc_l3( array, i1min,i1max, i2min,i2max, i3min,i3max, name, routine, copy, shrink )
END
SUBROUTINE realloc_l4( array, i1min,i1max, i2min,i2max, i3min,i3max, i4min,i4max, name, routine, copy, shrink )
END
SUBROUTINE realloc_l5( array, i1min,i1max, i2min,i2max, i3min,i3max, i4min, i4max, i5min, i5max, name, routine, copy, shrink )
END
SUBROUTINE realloc_i1s( array, i1max, name, routine, copy, shrink )
END
SUBROUTINE realloc_i2s( array, i1max, i2max, name, routine, copy, shrink )
END
SUBROUTINE realloc_i3s( array, i1max, i2max, i3max, name, routine, copy, shrink )
END
SUBROUTINE realloc_r1s( array, i1max, name, routine, copy, shrink )
END
SUBROUTINE realloc_r2s( array, i1max, i2max, name, routine, copy, shrink )
END
SUBROUTINE realloc_r3s( array, i1max, i2max, i3max, name, routine, copy, shrink )
END
SUBROUTINE realloc_r4s( array, i1max, i2max, i3max, i4max, name, routine, copy, shrink )
END
SUBROUTINE realloc_d1s( array, i1max, name, routine, copy, shrink )
END
SUBROUTINE realloc_d3s( array, i1max, i2max, i3max, name, routine, copy, shrink )
END
SUBROUTINE realloc_d4s( array, i1max, i2max, i3max, i4max, name, routine, copy, shrink )
END
SUBROUTINE realloc_l1s( array, i1max, name, routine, copy, shrink )
END
SUBROUTINE realloc_l2s( array, i1max, i2max, name, routine, copy, shrink )
END
SUBROUTINE realloc_l3s( array, i1max, i2max, i3max, name, routine, copy, shrink )
END
SUBROUTINE realloc_s1( array, i1min, i1max, name, routine, copy, shrink )
END
SUBROUTINE dealloc_i1( array, name, routine )
END
SUBROUTINE dealloc_i2( array, name, routine )
END
SUBROUTINE dealloc_i3( array, name, routine )
END
SUBROUTINE dealloc_i4( array, name, routine )
END
SUBROUTINE dealloc_i5( array, name, routine )
END
SUBROUTINE dealloc_E1( array, name, routine )
END
SUBROUTINE dealloc_r1( array, name, routine )
END
SUBROUTINE dealloc_r2( array, name, routine )
END
SUBROUTINE dealloc_r3( array, name, routine )
END
SUBROUTINE dealloc_r4( array, name, routine )
END
SUBROUTINE dealloc_r5( array, name, routine )
END
SUBROUTINE dealloc_d1( array, name, routine )
END
SUBROUTINE dealloc_d2( array, name, routine )
END
SUBROUTINE dealloc_d3( array, name, routine )
END
SUBROUTINE dealloc_d4( array, name, routine )
END
SUBROUTINE dealloc_d5( array, name, routine )
END
SUBROUTINE dealloc_c1( array, name, routine )
END
SUBROUTINE dealloc_c2( array, name, routine )
END
SUBROUTINE dealloc_c3( array, name, routine )
END
SUBROUTINE dealloc_c4( array, name, routine )
END
SUBROUTINE dealloc_c5( array, name, routine )
END
SUBROUTINE dealloc_z1( array, name, routine )
END
SUBROUTINE dealloc_z2( array, name, routine )
END
SUBROUTINE dealloc_z3( array, name, routine )
END
SUBROUTINE dealloc_z4( array, name, routine )
END
SUBROUTINE dealloc_z5( array, name, routine )
END
SUBROUTINE dealloc_l1( array, name, routine )
END
SUBROUTINE dealloc_l2( array, name, routine )
END
SUBROUTINE dealloc_l3( array, name, routine )
END
SUBROUTINE dealloc_l4( array, name, routine )
END
SUBROUTINE dealloc_l5( array, name, routine )
END
SUBROUTINE dealloc_s1( array, name, routine )
END
SUBROUTINE options( final_bounds, common_bounds, old_bounds, new_bounds, copy, shrink )
END
SUBROUTINE alloc_err( ierr, name, routine, bounds )
END
SUBROUTINE alloc_count( delta_size, type, name, routine )
END
END

View File

@ -0,0 +1,89 @@
module modfile73ba
use modfile73a, only: re_alloc, de_alloc
charactermod_name
type lData1D
integer refCount
character id
character name
end type
type TYPE_NAME
type(lData1D), pointer :: data => null()
end type
interface refcount
end interface
interface initialized
end interface
interface same
end interface
CONTAINS
subroutine init_(this)
end
subroutine delete_(this)
end
subroutine assign_(this, other)
end
function initialized_(thisinit)
end
function same_(this1,this2same)
end
function refcount_(thiscount)
end
function id_(thisstr)
end
function name_(this) result(str)
type(TYPE_NAME) this
character(len_trim(this%data%name)) str
end
subroutine tag_new_object(this)
end
subroutine delete_Data(a1d_data)
end
end
module modfile73bb
use modfile73a, only: re_alloc, de_alloc
charactermod_name
type lData1D
integer refCount
character id
character name
logical, pointer :: val => null()
end type
TYPE_NAME
type(lData1D), pointer :: data => null()
end type
PRIVATE
public TYPE_NAME
public initdelete, assignment, refcount, id
public name
public allocated
interface init
end interface
interface delete
end interface
interface initialized
subroutine die(str)
end
end interface
CONTAINS
subroutine init_(this)
end
subroutine delete_(this)
end
subroutine assign_(this, other)
end
function initialized_(thisinit)
end
function same_(this1,this2same)
end
function refcount_(thiscount)
end
function id_(thisstr)
end
function name_(thisstr)
end
subroutine tag_new_object(this)
end
subroutine delete_Data(a1d_data)
end
end

View File

@ -0,0 +1,4 @@
module modfile73c
type OrbitalDistribution_
end type
end

View File

@ -0,0 +1,53 @@
! This test verifies that both invocations produce a consistent order in the
! generated `.mod` file. Previous versions of Flang exhibited non-deterministic
! behavior due to iterating over a set ordered by heap pointers. This issue was
! particularly noticeable when using Flang as a library.
! RUN: rm -rf %t && mkdir -p %t
! RUN: %flang_fc1 \
! RUN: -fsyntax-only \
! RUN: -J%t \
! RUN: %S/Inputs/modfile73-a.f90 \
! RUN: %S/Inputs/modfile73-b.f90 \
! RUN: %S/Inputs/modfile73-c.f90
! RUN: %flang_fc1 -fsyntax-only -J%t %s
! RUN: cat %t/modfile73.mod | FileCheck %s
! RUN: rm -rf %t && mkdir -p %t
! RUN: %flang_fc1 \
! RUN: -fsyntax-only \
! RUN: -J%t \
! RUN: %S/Inputs/modfile73-a.f90 \
! RUN: %S/Inputs/modfile73-b.f90 \
! RUN: %S/Inputs/modfile73-c.f90 \
! RUN: %s
! RUN: cat %t/modfile73.mod | FileCheck %s
use modfile73ba
end
module modfile73
use modfile73bb
use modfile73c
CONTAINS
subroutine init_
end
subroutine delete_
end
subroutine assign_
end
function initialized_
end
function same_
end
function refcount_
end
function id_
end
function name_
end
subroutine tag_new_object
end
end
! CHECK: !need$ {{.*}} n modfile73bb
! CHECK-NEXT: !need$ {{.*}} n modfile73c