@@ -11,6 +11,7 @@ Author: Daniel Kroening, kroening@kroening.com
1111#include < util/arith_tools.h>
1212#include < util/c_types.h>
1313#include < util/expr_initializer.h>
14+ #include < util/find_symbols.h>
1415#include < util/namespace.h>
1516#include < util/prefix.h>
1617#include < util/std_code.h>
@@ -22,6 +23,130 @@ Author: Daniel Kroening, kroening@kroening.com
2223
2324#include < set>
2425
26+ using dependency_mapt =
27+ std::unordered_map<irep_idt, std::unordered_set<irep_idt>>;
28+
29+ // / Build a dependency graph for static lifetime objects.
30+ // / Returns a map from symbol identifier to the set of identifiers it depends
31+ // / on. According to C standard (C99/C11 Section 6.7.9 and 6.6 paragraph 9):
32+ // / - Objects with static storage duration can be initialized with constant
33+ // / expressions or string literals
34+ // / - An address constant is a pointer to an lvalue designating an object of
35+ // / static storage duration
36+ // / - When a static object's initializer references another static object,
37+ // / that referenced object must be initialized first to maintain proper
38+ // / initialization order
39+ static dependency_mapt build_static_initialization_dependencies (
40+ const std::set<std::string> &symbols,
41+ const namespacet &ns)
42+ {
43+ dependency_mapt dependencies;
44+
45+ for (const std::string &id : symbols)
46+ {
47+ const symbolt &symbol = ns.lookup (id);
48+
49+ // Only track dependencies for objects with static lifetime that have
50+ // initializers
51+ if (
52+ !symbol.is_static_lifetime || symbol.is_type || symbol.is_macro ||
53+ symbol.type .id () == ID_code || symbol.type .id () == ID_empty)
54+ {
55+ continue ;
56+ }
57+
58+ // Skip if no initializer or nondet initializer
59+ if (
60+ symbol.value .is_nil () ||
61+ (symbol.value .id () == ID_side_effect &&
62+ to_side_effect_expr (symbol.value ).get_statement () == ID_nondet))
63+ {
64+ continue ;
65+ }
66+
67+ // Find all symbols referenced in the initializer
68+ find_symbols_sett referenced_symbols =
69+ find_symbol_identifiers (symbol.value );
70+
71+ // Add dependencies on other static lifetime objects
72+ for (const irep_idt &ref_id : referenced_symbols)
73+ {
74+ // Skip self-references
75+ if (ref_id == symbol.name )
76+ continue ;
77+
78+ // Check if the referenced symbol is in our set of symbols to initialize
79+ if (symbols.find (id2string (ref_id)) == symbols.end ())
80+ continue ;
81+
82+ // Verify it's a static lifetime object
83+ if (ns.lookup (ref_id).is_static_lifetime )
84+ dependencies[symbol.name ].insert (ref_id);
85+ }
86+ }
87+
88+ return dependencies;
89+ }
90+
91+ // / Perform a topological sort on symbols considering their initialization
92+ // / dependencies. Returns a vector of symbol identifiers in initialization
93+ // / order. Uses alphabetical ordering as a tiebreaker for reproducibility.
94+ static std::vector<irep_idt> topological_sort_with_dependencies (
95+ const std::set<std::string> &symbols,
96+ const dependency_mapt &dependencies)
97+ {
98+ std::vector<irep_idt> result;
99+ std::unordered_set<irep_idt> visited;
100+ std::unordered_set<irep_idt> in_progress; // For cycle detection
101+
102+ // Recursive helper function for depth-first search
103+ std::function<void (const irep_idt &)> visit = [&](const irep_idt &id)
104+ {
105+ // If already visited, nothing to do
106+ if (visited.find (id) != visited.end ())
107+ return ;
108+
109+ // Check for cycles (should not happen in valid code)
110+ if (!in_progress.insert (id).second )
111+ {
112+ // Cycle detected - this indicates an error in the source code
113+ // For now, we'll just continue to avoid infinite recursion
114+ return ;
115+ }
116+
117+ // Visit all dependencies first
118+ auto dep_it = dependencies.find (id);
119+ if (dep_it != dependencies.end ())
120+ {
121+ // Sort dependencies alphabetically for reproducibility
122+ std::set<std::string> sorted_deps;
123+ for (const irep_idt &dep : dep_it->second )
124+ {
125+ sorted_deps.insert (id2string (dep));
126+ }
127+
128+ for (const std::string &dep : sorted_deps)
129+ {
130+ // Only visit if it's in our symbol set
131+ if (symbols.find (dep) != symbols.end ())
132+ visit (dep);
133+ }
134+ }
135+
136+ in_progress.erase (id);
137+ visited.insert (id);
138+ result.push_back (id);
139+ };
140+
141+ // Process all symbols in alphabetical order for reproducibility
142+ for (const std::string &id : symbols)
143+ {
144+ visit (id);
145+ }
146+
147+ return result;
148+ }
149+
25150static std::optional<codet> static_lifetime_init (
26151 const irep_idt &identifier,
27152 symbol_table_baset &symbol_table)
@@ -116,31 +241,65 @@ void static_lifetime_init(
116241
117242 // do assignments based on "value"
118243
119- // sort alphabetically for reproducible results
244+ // Build dependency graph for static lifetime initialization.
245+ // According to the C standard (C99/C11):
246+ // - Section 6.7.9 paragraph 4: All expressions in an initializer for an
247+ // object that has static storage duration shall be constant expressions
248+ // or string literals.
249+ // - Section 6.6 paragraph 9: An address constant is a null pointer, a pointer
250+ // to an lvalue designating an object of static storage duration, or a
251+ // pointer to a function designator.
252+ // - Section 6.2.4: Objects with static storage duration are initialized
253+ // before program startup.
254+ //
255+ // When one static object's initializer takes the address of another static
256+ // object, the referenced object must be initialized first to ensure the
257+ // address constant is properly available.
258+
259+ // First, collect all symbols and sort alphabetically for reproducible results
120260 std::set<std::string> symbols;
121261
122262 for (const auto &symbol_pair : symbol_table.symbols )
123263 {
124264 symbols.insert (id2string (symbol_pair.first ));
125265 }
126266
127- // first do framework variables
267+ // Build dependency graph
268+ auto dependencies = build_static_initialization_dependencies (symbols, ns);
269+
270+ // Separate CPROVER framework variables from user variables
271+ std::set<std::string> cprover_symbols;
272+ std::set<std::string> user_symbols;
273+
128274 for (const std::string &id : symbols)
275+ {
129276 if (has_prefix (id, CPROVER_PREFIX))
130- {
131- auto code = static_lifetime_init (id, symbol_table);
132- if (code.has_value ())
133- dest.add (std::move (*code));
134- }
277+ cprover_symbols.insert (id);
278+ else
279+ user_symbols.insert (id);
280+ }
135281
136- // now all other variables
137- for (const std::string &id : symbols)
138- if (!has_prefix (id, CPROVER_PREFIX))
139- {
140- auto code = static_lifetime_init (id, symbol_table);
141- if (code.has_value ())
142- dest.add (std::move (*code));
143- }
282+ // First initialize framework variables with topological sort
283+ std::vector<irep_idt> sorted_cprover =
284+ topological_sort_with_dependencies (cprover_symbols, dependencies);
285+
286+ for (const auto &id : sorted_cprover)
287+ {
288+ auto code = static_lifetime_init (id, symbol_table);
289+ if (code.has_value ())
290+ dest.add (std::move (*code));
291+ }
292+
293+ // Now initialize all other variables with topological sort
294+ std::vector<irep_idt> sorted_user =
295+ topological_sort_with_dependencies (user_symbols, dependencies);
296+
297+ for (const auto &id : sorted_user)
298+ {
299+ auto code = static_lifetime_init (id, symbol_table);
300+ if (code.has_value ())
301+ dest.add (std::move (*code));
302+ }
144303
145304 // now call designated "initialization" functions
146305
0 commit comments