|
11 | 11 | #include <util/arith_tools.h> |
12 | 12 | #include <util/c_types.h> |
13 | 13 | #include <util/expr_initializer.h> |
| 14 | +#include <util/find_symbols.h> |
14 | 15 | #include <util/namespace.h> |
15 | 16 | #include <util/prefix.h> |
16 | 17 | #include <util/std_code.h> |
|
22 | 23 |
|
23 | 24 | #include <set> |
24 | 25 |
|
| 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 | + |
25 | 150 | static std::optional<codet> static_lifetime_init( |
26 | 151 | const irep_idt &identifier, |
27 | 152 | symbol_table_baset &symbol_table) |
@@ -116,31 +241,65 @@ void static_lifetime_init( |
116 | 241 |
|
117 | 242 | // do assignments based on "value" |
118 | 243 |
|
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 |
120 | 260 | std::set<std::string> symbols; |
121 | 261 |
|
122 | 262 | for(const auto &symbol_pair : symbol_table.symbols) |
123 | 263 | { |
124 | 264 | symbols.insert(id2string(symbol_pair.first)); |
125 | 265 | } |
126 | 266 |
|
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 | + |
128 | 274 | for(const std::string &id : symbols) |
| 275 | + { |
129 | 276 | 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 | + } |
135 | 281 |
|
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 | + } |
144 | 303 |
|
145 | 304 | // now call designated "initialization" functions |
146 | 305 |
|
|
0 commit comments