Skip to content

Commit a390ae0

Browse files
author
Owen Jones
committed
Use strong writes for precise EVSs when applying function summaries
1 parent 5f4eb14 commit a390ae0

File tree

3 files changed

+104
-79
lines changed

3 files changed

+104
-79
lines changed

regression/LVSA/TestEVS/test_evs.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,11 +94,9 @@ def test_write_to_field_of_B_through_A(tmpdir):
9494

9595
value_set_expectation = lvsa_expectation.get_value_set_for_public_static('static_node')
9696

97-
value_set_expectation.check_number_of_values(3)
97+
value_set_expectation.check_number_of_values(2)
9898
value_set_expectation.check_contains_root_object_evs(label_suffix='node_parameter')
9999
value_set_expectation.check_contains_per_field_evs(access_path=['.node'])
100-
value_set_expectation.check_contains_precise_evs(label_suffix='b', access_path=['.node'],
101-
decl_on_types=['LVSA.TestEVS.Test$A'])
102100

103101

104102
def test_apply_call_overridden_method_on_A_with_A(tmpdir):

regression/LVSA/TestPreciseAccessPaths/test_precise_access_paths.py

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,8 @@ def test_apply_set_field_of_external_object(tmpdir):
6969
value_set_expectation = lvsa_expectation.get_value_set_for_precise_evs(
7070
parameter_name='parameter_a', suffix='.object')
7171

72-
value_set_expectation.check_number_of_values(2)
72+
value_set_expectation.check_number_of_values(1)
7373
value_set_expectation.check_contains_root_object_evs(label_suffix='parameter_object')
74-
value_set_expectation.check_contains_precise_evs(label_suffix='parameter_a', access_path=['.object'],
75-
is_initializer=True)
7674

7775

7876
def test_conditionally_set_field_of_external_object(tmpdir):
@@ -95,15 +93,11 @@ def test_apply_conditionally_set_field_of_external_object(tmpdir):
9593
value_set_expectation = lvsa_expectation.get_value_set_for_precise_evs(
9694
parameter_name='parameter_a', suffix='.object')
9795

98-
value_set_expectation.check_number_of_values(4)
99-
# Two values that existed before the function call
96+
value_set_expectation.check_number_of_values(3)
10097
value_set_expectation.check_contains_per_field_evs(access_path=['.object'])
10198
value_set_expectation.check_contains_precise_evs(label_suffix='parameter_a', access_path=['.object'],
10299
is_initializer=False)
103-
# Two values that came from the function call
104100
value_set_expectation.check_contains_root_object_evs(label_suffix='parameter_object')
105-
value_set_expectation.check_contains_precise_evs(label_suffix='parameter_a', access_path=['.object'],
106-
is_initializer=True)
107101

108102

109103
def test_read_field_before_writing_to_aliasable_field(tmpdir):
@@ -176,9 +170,8 @@ def test_call_function_to_allocate_new_object(tmpdir):
176170

177171
value_set_expectation = lvsa_expectation.get_value_set_for_output()
178172

179-
value_set_expectation.check_number_of_values(3)
173+
value_set_expectation.check_number_of_values(2)
180174
value_set_expectation.check_contains_per_field_evs(access_path=['.object'])
181-
value_set_expectation.check_contains_precise_evs(label_suffix='param_A', access_path=['.object'])
182175
value_set_expectation.check_contains_dynamic_object()
183176

184177

@@ -268,7 +261,6 @@ def test_apply_array_deref(tmpdir):
268261
# value_set_expectation.check_contains_per_field_evs(access_path=['.object'], is_initializer=True)
269262

270263

271-
@pytest.mark.xfail(strict=True)
272264
def test_check_strong_writes_for_precise_evs(tmpdir):
273265
lvsa_driver = LvsaDriver(tmpdir, folder_name).with_test_function('check_strong_writes_for_precise_evs')
274266
lvsa_expectation = lvsa_driver.run()
@@ -279,7 +271,6 @@ def test_check_strong_writes_for_precise_evs(tmpdir):
279271
value_set_expectation.check_contains_dynamic_object(1)
280272

281273

282-
@pytest.mark.xfail(strict=True)
283274
def test_check_aliasing_function_arguments(tmpdir):
284275
lvsa_driver = LvsaDriver(tmpdir, folder_name).with_test_function('check_aliasing_function_arguments')
285276
lvsa_expectation = lvsa_driver.run()

src/pointer-analysis/local_value_set_analysis.cpp

Lines changed: 100 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -140,13 +140,14 @@ void local_value_set_analysist::initialize(const goto_programt &fun)
140140
/// \param declared_on_type: if not nil, filter by LHS object type
141141
/// \param state: value set to search. Non-const as we will derive non-const
142142
/// pointers into its internal data structures.
143-
/// \param [out] entrylist: extended with pointers into `state.values` for each
144-
/// matching entry. Note the pointers are non-const.
143+
/// \param [out] key_list: extended with keys in state.values for each matching
144+
/// entry
145145
static void get_all_field_value_sets(
146146
const std::string &fieldname,
147147
const typet &declared_on_type,
148148
local_value_sett &state,
149-
std::vector<local_value_sett::entryt *> &entrylist)
149+
const namespacet &ns,
150+
std::vector<irep_idt> &key_list)
150151
{
151152
// Find all value sets we're currently aware of that may be affected by
152153
// a write to the given field:
@@ -163,7 +164,7 @@ static void get_all_field_value_sets(
163164
else if(entry.second.declared_on_type == declared_on_type)
164165
include_this = true;
165166
if(include_this)
166-
entrylist.push_back(&entry.second);
167+
key_list.push_back(entry.second.get_key(ns));
167168
}
168169
}
169170
}
@@ -407,12 +408,18 @@ void local_value_set_analysist::transform_function_stub(
407408

408409
std::map<exprt, local_value_sett::object_mapt> pre_call_rhs_value_sets;
409410

411+
local_value_sett::valuest weak_updates;
412+
std::vector<std::pair<exprt, value_sett::object_mapt>> strong_updates;
413+
410414
for(const auto &assignment : assignments)
411415
{
412416
++nstub_assignments;
417+
418+
// First evaluate the right hand side of this assignment, with caching to
419+
// avoid unnecessary recalculations
413420
const auto &rhs_expr = assignment.second;
414-
if(pre_call_rhs_value_sets.count(rhs_expr))
415-
continue;
421+
if(!pre_call_rhs_value_sets.count(rhs_expr))
422+
{
416423
value_sett::object_mapt &rhs_values = pre_call_rhs_value_sets[rhs_expr];
417424
if(rhs_expr.id() == ID_external_value_set)
418425
{
@@ -422,29 +429,31 @@ void local_value_set_analysist::transform_function_stub(
422429
const std::string &evse_label =
423430
id2string(to_constant_expr(evse.label()).get_value());
424431

425-
if(has_prefix(evse_label, PER_FIELD_EVS_PREFIX))
426-
{
427-
PRECONDITION(evse.access_path_entries().size() == 1);
428-
429-
// This external value set denotes either all pointers stored in a
430-
// particular field (e.g. TypeA.fieldb), or all arrays (in which case
431-
// declared_on_type() is nil the the field name is "[]")
432-
std::vector<local_value_sett::entryt *> rhs_entries;
433-
const auto &apback = evse.access_path_entries().back();
434-
if(!evse.is_initializer())
432+
if(has_prefix(evse_label, PER_FIELD_EVS_PREFIX))
435433
{
436-
get_all_field_value_sets(
437-
id2string(apback.label()),
438-
apback.declared_on_type(),
439-
valuesets,
440-
rhs_entries);
441-
}
442-
443-
// Accumulate all possibly-read pointers into `rhs_values`:
444-
for(const auto &rhs_entry : rhs_entries)
445-
{
446-
valuesets.make_union_adjusting_types(
447-
rhs_values, rhs_entry->object_map, evse.type(), ns);
434+
PRECONDITION(evse.access_path_entries().size() == 1);
435+
436+
// This external value set denotes either all pointers stored in a
437+
// particular field (e.g. TypeA.fieldb), or all arrays (in which case
438+
// declared_on_type() is nil the the field name is "[]")
439+
std::vector<irep_idt> key_list;
440+
const auto &apback = evse.access_path_entries().back();
441+
if(!evse.is_initializer())
442+
{
443+
get_all_field_value_sets(
444+
id2string(apback.label()),
445+
apback.declared_on_type(),
446+
valuesets,
447+
ns,
448+
key_list);
449+
}
450+
451+
// Accumulate all possibly-read pointers into `rhs_values`:
452+
for(const auto &key : key_list)
453+
{
454+
local_value_sett::entryt &entry = valuesets.values.at(key);
455+
valuesets.make_union_adjusting_types(
456+
rhs_values, entry.object_map, evse.type(), ns);
448457
}
449458
// Also add the external set itself,
450459
// representing the possibility that the read
@@ -472,37 +481,37 @@ void local_value_set_analysist::transform_function_stub(
472481

473482
valuesets.get_value_set(root_object_expr, rhs_values, ns, false);
474483
}
475-
}
476-
else
477-
{
478-
// Ordinary value set member (not an external value set);
479-
480-
const auto rhs_num = valuesets.object_numbering.get_number(rhs_expr);
481-
if(can_cast_expr<dynamic_object_exprt>(rhs_expr) && rhs_num)
482-
{
483-
unsigned loc_number =
484-
expr_try_dynamic_cast<dynamic_object_exprt>(rhs_expr)->get_instance();
485-
valuesets.demote_most_recent_dynamic_objects(loc_number);
486484
}
485+
else
486+
{
487+
// Ordinary value set member (not an external value set);
488+
489+
const auto rhs_num = valuesets.object_numbering.get_number(rhs_expr);
490+
if(can_cast_expr<dynamic_object_exprt>(rhs_expr) && rhs_num)
491+
{
492+
unsigned loc_number =
493+
expr_try_dynamic_cast<dynamic_object_exprt>(rhs_expr)
494+
->get_instance();
495+
valuesets.demote_most_recent_dynamic_objects(loc_number);
496+
}
487497

488-
valuesets.insert(rhs_values, rhs_expr);
498+
valuesets.insert(rhs_values, rhs_expr);
499+
}
489500
}
490-
}
491501

492-
// OK, all RHS sets have been read, now assign to the LHS symbols:
502+
// Now calculate the left hand side of the assignment and store the update
503+
// from the lhs to the rhs
493504

494-
for(const auto &assignment : assignments)
495-
{
496505
const auto &lhs_fieldname = assignment.first;
497-
const auto &rhs_expr = assignment.second;
498-
const auto &rhs_values = pre_call_rhs_value_sets.at(rhs_expr);
506+
const value_sett::object_mapt &rhs_values =
507+
pre_call_rhs_value_sets.at(rhs_expr);
499508

500509
if(has_prefix(lhs_fieldname.base_name, PER_FIELD_EVS_PREFIX))
501510
{
502511
// This writes to an external value set that denotes all fields with
503512
// a particular name and declaring type.
504513
// Apply the write to all matching local objects too:
505-
std::vector<local_value_sett::entryt *> lhs_entries;
514+
std::vector<irep_idt> key_list;
506515

507516
const bool rhs_is_initializer_per_field_evs =
508517
rhs_expr.id() == ID_external_value_set &&
@@ -515,7 +524,8 @@ void local_value_set_analysist::transform_function_stub(
515524
lhs_fieldname.field_name,
516525
lhs_fieldname.declared_on_type,
517526
valuesets,
518-
lhs_entries);
527+
ns,
528+
key_list);
519529
}
520530
// Also write to the external value set itself, representing writes
521531
// whose effects are external to this context too:
@@ -524,14 +534,17 @@ void local_value_set_analysist::transform_function_stub(
524534
lhs_fieldname.field_name,
525535
lhs_fieldname.declared_on_type,
526536
lhs_fieldname.structured_lhs);
527-
std::string objkey = lhs_fieldname.base_name + lhs_fieldname.field_name;
528-
auto insertit =
529-
valuesets.values.insert(std::make_pair(objkey, evse_entry));
530-
lhs_entries.push_back(&insertit.first->second);
537+
irep_idt objkey = evse_entry.get_key(ns);
538+
valuesets.values.insert(std::make_pair(objkey, evse_entry));
539+
key_list.emplace_back(objkey);
531540

532-
// Apply the write to `state`:
533-
for(auto lhs_entry : lhs_entries)
534-
valuesets.make_union(lhs_entry->object_map, rhs_values);
541+
// Store the write to `weak_updates`:
542+
for(const irep_idt &key : key_list)
543+
{
544+
auto insert_it =
545+
weak_updates.insert(std::make_pair(key, valuesets.values.at(key)));
546+
valuesets.make_union(insert_it.first->second.object_map, rhs_values);
547+
}
535548
}
536549
else if(has_prefix(lhs_fieldname.base_name, PRECISE_EVS_PREFIX))
537550
{
@@ -544,11 +557,9 @@ void local_value_set_analysist::transform_function_stub(
544557
exprt lhs_expr = get_expr_from_precise_evs(
545558
precise_evs, function_symbol, fcall, ns, *this);
546559

547-
// Weak write - can we do strong writes?
548-
bool add_to_sets = true;
549-
auto demoted_rhs_values = pre_call_rhs_value_sets.at(rhs_expr);
550-
valuesets.adjust_assign_rhs_values(rhs_expr, ns, demoted_rhs_values);
551-
valuesets.assign_valueset(lhs_expr, demoted_rhs_values, ns, add_to_sets);
560+
strong_updates.emplace_back(lhs_expr, rhs_values);
561+
valuesets.adjust_assign_rhs_values(
562+
rhs_expr, ns, strong_updates.back().second);
552563
}
553564
else if(has_prefix(lhs_fieldname.base_name, prefix_dynamic_object))
554565
{
@@ -560,7 +571,7 @@ void local_value_set_analysist::transform_function_stub(
560571
lhs_fieldname.declared_on_type,
561572
lhs_fieldname.structured_lhs);
562573
auto insertit =
563-
valuesets.values.insert(std::make_pair(objkey, dynobj_entry_name));
574+
weak_updates.insert(std::make_pair(objkey, dynobj_entry_name));
564575
valuesets.make_union(insertit.first->second.object_map, rhs_values);
565576
}
566577
else
@@ -569,15 +580,40 @@ void local_value_set_analysist::transform_function_stub(
569580
// variables. Global variables defined in the java source cannot be
570581
// structs and thus will have an empty string as the field name, but
571582
// this is not true for synthetic global variables, such as class models.
572-
local_value_sett::entryt global_entry_name(
583+
local_value_sett::entryt global_entry(
573584
lhs_fieldname.base_name,
574585
lhs_fieldname.field_name,
575586
lhs_fieldname.declared_on_type,
576587
lhs_fieldname.structured_lhs);
577-
auto &global_entry = valuesets.get_entry(global_entry_name, ns);
578-
valuesets.make_union(global_entry.object_map, rhs_values);
588+
auto insert_it = weak_updates.insert(
589+
std::make_pair(global_entry.get_key(ns), global_entry));
590+
591+
valuesets.make_union(insert_it.first->second.object_map, rhs_values);
579592
}
580593
}
594+
595+
// Now apply the updates. We have to be careful that parameters might alias,
596+
// so we have to deal with strong updates in a slightly roundabout way. We
597+
// first write an empty object map to expr which got a strong update. If the
598+
// expr is singular then this will erase the object map. Then we write the
599+
// intended object map with `add_to_sets` false, so the write is weak. Then we
600+
// apply the weak updates.
601+
value_sett::object_mapt empty_object_map;
602+
for(auto &strong_update : strong_updates)
603+
{
604+
const bool add_to_sets = false;
605+
valuesets.assign_valueset(
606+
strong_update.first, empty_object_map, ns, add_to_sets);
607+
}
608+
609+
for(auto &strong_update : strong_updates)
610+
{
611+
const bool add_to_sets = true;
612+
valuesets.assign_valueset(
613+
strong_update.first, strong_update.second, ns, add_to_sets);
614+
}
615+
616+
valuesets.make_union(weak_updates);
581617
}
582618

583619
void local_value_set_analysist::save_summary(const goto_programt &goto_program)

0 commit comments

Comments
 (0)