Skip to content

Commit 3dbd7ba

Browse files
committed
L1 renaming at each declaration
This was done up until c8f0f7b. The (observable) problem tackled here is that objects declared in loops got the same L1 name, which implies they had the same address. Thus objects were spuriously reported as dead when their address was taken, because a pointer to the latest (live) object would be the same as one to an earlier (genuinely dead) object. A side effect of this change is objects generated for test suites now have L1 indices, as witnessed in the pointer-function-parameters test.
1 parent 7e8bf45 commit 3dbd7ba

File tree

11 files changed

+93
-72
lines changed

11 files changed

+93
-72
lines changed

regression/cbmc-cover/pointer-function-parameters/test.desc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ main.c
44
^\*\* 5 of 5 covered \(100\.0%\)$
55
^Test suite:$
66
^(tmp(\$\d+)?=[^,]*, a=\(\(signed int \*\)NULL\))|(a=\(\(signed int \*\)NULL\), tmp(\$\d+)?=[^,]*)$
7-
^(a=&tmp(\$\d+)?!0, tmp(\$\d+)?=4)|(tmp(\$\d+)?=4, a=&tmp(\$\d+)?!0)$
8-
^(a=&tmp(\$\d+)?!0, tmp(\$\d+)?=([012356789][0-9]*|4[0-9]+))|(tmp(\$\d+)?=([012356789][0-9]*|4[0-9]+), a=&tmp(\$\d+)?!0)$
7+
^(a=&tmp(\$\d+)?!0@1, tmp(\$\d+)?=4)|(tmp(\$\d+)?=4, a=&tmp(\$\d+)?!0@1)$
8+
^(a=&tmp(\$\d+)?!0@1, tmp(\$\d+)?=([012356789][0-9]*|4[0-9]+))|(tmp(\$\d+)?=([012356789][0-9]*|4[0-9]+), a=&tmp(\$\d+)?!0@1)$
99
^EXIT=0$
1010
^SIGNAL=0$
1111
--
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
int g;
2+
3+
int main()
4+
{
5+
for(int i = 0; i < 4; ++i)
6+
{
7+
int *x;
8+
*&x = &g;
9+
}
10+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CORE
2+
main.c
3+
--pointer-check
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
--
8+
^warning: ignoring

regression/cbmc/vla3/test.desc

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,4 @@ main.c
77
--
88
^warning: ignoring
99
--
10-
Renaming is inconsistent across loop iterations as we map L1 names to types, but
11-
do not actually introduce new L1 ids each time we declare an object.
10+
The array decision procedure does not yet handle member expressions.

src/goto-programs/goto_clean_expr.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ symbol_exprt goto_convertt::make_compound_literal(
4545

4646
// The lifetime of compound literals is really that of
4747
// the block they are in.
48-
copy(code_declt(result), DECL, dest);
48+
if(!new_symbol.is_static_lifetime)
49+
copy(code_declt(result), DECL, dest);
4950

5051
code_assignt code_assign(result, expr);
5152
code_assign.add_source_location()=source_location;

src/goto-symex/goto_symex_state.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -785,3 +785,29 @@ void goto_symex_statet::print_backtrace(std::ostream &out) const
785785
<< frame.calling_location.pc->location_number << "\n";
786786
}
787787
}
788+
789+
void goto_symex_statet::add_object(
790+
ssa_exprt &ssa,
791+
std::size_t l1_index,
792+
const namespacet &ns)
793+
{
794+
framet &frame = call_stack().top();
795+
796+
const irep_idt l0_name = ssa.get_identifier();
797+
798+
// save old L1 name, if any
799+
auto existing_or_new_entry = level1.current_names.emplace(
800+
std::piecewise_construct,
801+
std::forward_as_tuple(l0_name),
802+
std::forward_as_tuple(ssa, l1_index));
803+
804+
if(!existing_or_new_entry.second)
805+
{
806+
frame.old_level1.emplace(l0_name, existing_or_new_entry.first->second);
807+
existing_or_new_entry.first->second = std::make_pair(ssa, l1_index);
808+
}
809+
810+
ssa = rename_ssa<L1>(std::move(ssa), ns);
811+
const bool inserted = frame.local_objects.insert(ssa.get_identifier()).second;
812+
INVARIANT(inserted, "l1_name expected to be unique by construction");
813+
}

src/goto-symex/goto_symex_state.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,6 @@ class goto_symex_statet final : public goto_statet
6060

6161
symex_target_equationt *symex_target;
6262

63-
// we remember all L1 renamings
64-
std::set<irep_idt> l1_history;
65-
6663
symex_level0t level0;
6764
symex_level1t level1;
6865

@@ -146,6 +143,10 @@ class goto_symex_statet final : public goto_statet
146143
return threads[source.thread_nr].call_stack;
147144
}
148145

146+
/// Turn L0-renamed \p ssa into an L1-renamed SSA expression with an L1 index
147+
/// \p l1_index that must not have previously been used.
148+
void add_object(ssa_exprt &ssa, std::size_t l1_index, const namespacet &ns);
149+
149150
void print_backtrace(std::ostream &) const;
150151

151152
// threads

src/goto-symex/path_storage.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,11 +98,24 @@ class path_storaget
9898
/// error-handling paths.
9999
std::unordered_map<irep_idt, local_safe_pointerst> safe_pointers;
100100

101+
/// Provide a unique index for a given \p id, starting from \p minimum_index.
102+
std::size_t get_unique_index(const irep_idt &id, std::size_t minimum_index)
103+
{
104+
auto entry = unique_index_map.emplace(id, minimum_index);
105+
106+
if(!entry.second)
107+
entry.first->second = std::max(entry.first->second + 1, minimum_index);
108+
109+
return entry.first->second;
110+
}
111+
101112
private:
102113
// Derived classes should override these methods, allowing the base class to
103114
// enforce preconditions.
104115
virtual patht &private_peek() = 0;
105116
virtual void private_pop() = 0;
117+
118+
std::unordered_map<irep_idt, std::size_t> unique_index_map;
106119
};
107120

108121
/// \brief LIFO save queue: depth-first search, try to finish paths

src/goto-symex/symex_decl.cpp

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,16 @@ void goto_symext::symex_decl(statet &state)
3434

3535
void goto_symext::symex_decl(statet &state, const symbol_exprt &expr)
3636
{
37-
// We increase the L2 renaming to make these non-deterministic.
38-
// We also prevent propagation of old values.
39-
40-
ssa_exprt ssa = state.rename_ssa<L1>(ssa_exprt{expr}, ns);
37+
// each declaration introduces a new object, which we record as a fresh L1
38+
// index
39+
40+
ssa_exprt ssa = state.rename_ssa<L0>(ssa_exprt{expr}, ns);
41+
// We use "1" as the first level-1 index, which is in line with doing so for
42+
// level-2 indices (but it's an arbitrary choice, we have just always been
43+
// doing it this way).
44+
const std::size_t fresh_l1_index =
45+
path_storage.get_unique_index(ssa.get_identifier(), 1);
46+
state.add_object(ssa, fresh_l1_index, ns);
4147
const irep_idt &l1_identifier = ssa.get_identifier();
4248

4349
// rename type to L2
@@ -57,16 +63,11 @@ void goto_symext::symex_decl(statet &state, const symbol_exprt &expr)
5763
state.value_set.assign(ssa, l1_rhs, ns, true, false);
5864
}
5965

60-
// prevent propagation
61-
state.propagation.erase(l1_identifier);
62-
6366
// L2 renaming
64-
// inlining may yield multiple declarations of the same identifier
65-
// within the same L1 context
66-
const auto level2_it =
67-
state.level2.current_names.emplace(l1_identifier, std::make_pair(ssa, 0))
68-
.first;
69-
symex_renaming_levelt::increase_counter(level2_it);
67+
bool is_new =
68+
state.level2.current_names.emplace(l1_identifier, std::make_pair(ssa, 1))
69+
.second;
70+
CHECK_RETURN(is_new);
7071
const bool record_events=state.record_events;
7172
state.record_events=false;
7273
exprt expr_l2 = state.rename(std::move(ssa), ns);

src/goto-symex/symex_function_call.cpp

Lines changed: 10 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ Author: Daniel Kroening, [email protected]
2222
static void locality(
2323
const irep_idt &function_identifier,
2424
goto_symext::statet &state,
25+
path_storaget &path_storage,
2526
const goto_functionst::goto_functiont &goto_function,
2627
const namespacet &ns);
2728

@@ -306,7 +307,7 @@ void goto_symext::symex_function_call_code(
306307
framet &frame = state.call_stack().new_frame(state.source);
307308

308309
// preserve locality of local variables
309-
locality(identifier, state, goto_function, ns);
310+
locality(identifier, state, path_storage, goto_function, ns);
310311

311312
// assign actuals to formal parameters
312313
parameter_assignments(identifier, goto_function, state, arguments);
@@ -382,68 +383,27 @@ void goto_symext::symex_end_of_function(statet &state)
382383
pop_frame(state);
383384
}
384385

385-
/// preserves locality of local variables of a given function by applying L1
386-
/// renaming to the local identifiers
386+
/// Preserves locality of parameters of a given function by applying L1
387+
/// renaming to them.
387388
static void locality(
388389
const irep_idt &function_identifier,
389390
goto_symext::statet &state,
391+
path_storaget &path_storage,
390392
const goto_functionst::goto_functiont &goto_function,
391393
const namespacet &ns)
392394
{
393395
unsigned &frame_nr=
394396
state.threads[state.source.thread_nr].function_frame[function_identifier];
395397
frame_nr++;
396398

397-
std::set<irep_idt> local_identifiers;
398-
399-
get_local_identifiers(goto_function, local_identifiers);
400-
401-
framet &frame = state.call_stack().top();
402-
403-
for(std::set<irep_idt>::const_iterator
404-
it=local_identifiers.begin();
405-
it!=local_identifiers.end();
406-
it++)
399+
for(const auto &param : goto_function.parameter_identifiers)
407400
{
408401
// get L0 name
409402
ssa_exprt ssa =
410-
state.rename_ssa<L0>(ssa_exprt{ns.lookup(*it).symbol_expr()}, ns);
411-
const irep_idt l0_name=ssa.get_identifier();
412-
413-
// save old L1 name for popping the frame
414-
auto c_it = state.level1.current_names.find(l0_name);
415-
416-
if(c_it != state.level1.current_names.end())
417-
{
418-
frame.old_level1.emplace(l0_name, c_it->second);
419-
c_it->second = std::make_pair(ssa, frame_nr);
420-
}
421-
else
422-
{
423-
c_it = state.level1.current_names
424-
.emplace(l0_name, std::make_pair(ssa, frame_nr))
425-
.first;
426-
}
427-
428-
// do L1 renaming -- these need not be unique, as
429-
// identifiers may be shared among functions
430-
// (e.g., due to inlining or other code restructuring)
431-
432-
ssa_exprt ssa_l1 = state.rename_ssa<L1>(std::move(ssa), ns);
433-
434-
irep_idt l1_name = ssa_l1.get_identifier();
435-
unsigned offset=0;
436-
437-
while(state.l1_history.find(l1_name)!=state.l1_history.end())
438-
{
439-
symex_renaming_levelt::increase_counter(c_it);
440-
++offset;
441-
ssa_l1.set_level_1(frame_nr + offset);
442-
l1_name = ssa_l1.get_identifier();
443-
}
403+
state.rename_ssa<L0>(ssa_exprt{ns.lookup(param).symbol_expr()}, ns);
444404

445-
// now unique -- store
446-
frame.local_objects.insert(l1_name);
447-
state.l1_history.insert(l1_name);
405+
const std::size_t fresh_l1_index =
406+
path_storage.get_unique_index(ssa.get_identifier(), frame_nr);
407+
state.add_object(ssa, fresh_l1_index, ns);
448408
}
449409
}

src/goto-symex/symex_start_thread.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ void goto_symext::symex_start_thread(statet &state)
6666

6767
// get L0 name for current thread
6868
lhs.set_level_0(t);
69+
const irep_idt &l0_name = lhs.get_identifier();
70+
std::size_t l1_index = path_storage.get_unique_index(l0_name, 0);
71+
CHECK_RETURN(l1_index == 0);
6972

7073
// set up L1 name
7174
auto emplace_result = state.level1.current_names.emplace(
@@ -76,7 +79,6 @@ void goto_symext::symex_start_thread(statet &state)
7679
const ssa_exprt lhs_l1 = state.rename_ssa<L1>(std::move(lhs), ns);
7780
const irep_idt l1_name = lhs_l1.get_l1_object_identifier();
7881
// store it
79-
state.l1_history.insert(l1_name);
8082
new_thread.call_stack.back().local_objects.insert(l1_name);
8183

8284
// make copy

0 commit comments

Comments
 (0)