Skip to content

Commit 1e8e495

Browse files
authored
Merge pull request #6459 from remi-delmas-3000/assigns-clause-perf-optim
Disable unnecessary pointer checks, enable necessary pointer checks i…
2 parents adfd71d + e2f4fec commit 1e8e495

File tree

7 files changed

+98
-50
lines changed

7 files changed

+98
-50
lines changed

regression/contracts/assigns_enforce_free_dead/test.desc

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@ main.c
2929
^\[foo.\d+\] line \d+ Check that y is assignable: FAILURE$
3030
^\[foo.\d+\] line \d+ Check that z is assignable: FAILURE$
3131
^\[foo.\d+\] line \d+ Check that POINTER_OBJECT\(\(void \*\)z\) is assignable: FAILURE$
32-
^.*tmp_cc\$\d+.*: FAILURE$
32+
^.*__car_valid.*: FAILURE$
33+
^.*__car_lb.*: FAILURE$
34+
^.*__car_ub.*: FAILURE$
3335
--
3436
Checks that invalidated CARs are correctly tracked on `free` and `DEAD`.
3537

@@ -40,4 +42,4 @@ We also check (using positive regex matches above) that
4042
`**p` should be assignable in one case and should not be assignable in the other.
4143

4244
Finally, we check that there should be no "internal" assertion failures
43-
on `tmp_cc` variables used to track CARs.
45+
on `__car_valid`, `__car_ub`, `__car_lb` variables used to track CARs.

regression/contracts/quantifiers-forall-ensures-enforce/main.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ int f1(int *arr, int len)
2323
int main()
2424
{
2525
int len;
26-
__CPROVER_assume(0 <= len && len <= MAX_LEN);
26+
__CPROVER_assume(0 < len && len <= MAX_LEN);
2727

2828
int *arr = malloc(len * sizeof(int));
2929
f1(arr, len);

src/goto-instrument/contracts/assigns.cpp

Lines changed: 41 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ Date: July 2021
1212
/// Specify write set in code contracts
1313

1414
#include "assigns.h"
15+
#include "utils.h"
1516

1617
#include <analyses/call_graph.h>
1718

@@ -52,14 +53,16 @@ static const slicet normalize_to_slice(const exprt &expr, const namespacet &ns)
5253
}
5354

5455
const symbolt assigns_clauset::conditional_address_ranget::generate_new_symbol(
56+
const std::string &prefix,
5557
const typet &type,
5658
const source_locationt &location) const
5759
{
5860
return new_tmp_symbol(
5961
type,
6062
location,
6163
parent.symbol_table.lookup_ref(parent.function_name).mode,
62-
parent.symbol_table);
64+
parent.symbol_table,
65+
prefix);
6366
}
6467

6568
assigns_clauset::conditional_address_ranget::conditional_address_ranget(
@@ -70,11 +73,13 @@ assigns_clauset::conditional_address_ranget::conditional_address_ranget(
7073
parent(parent),
7174
slice(normalize_to_slice(expr, parent.ns)),
7275
validity_condition_var(
73-
generate_new_symbol(bool_typet(), location).symbol_expr()),
76+
generate_new_symbol("__car_valid", bool_typet(), location).symbol_expr()),
7477
lower_bound_address_var(
75-
generate_new_symbol(slice.first.type(), location).symbol_expr()),
78+
generate_new_symbol("__car_lb", slice.first.type(), location)
79+
.symbol_expr()),
7680
upper_bound_address_var(
77-
generate_new_symbol(slice.first.type(), location).symbol_expr())
81+
generate_new_symbol("__car_ub", slice.first.type(), location)
82+
.symbol_expr())
7883
{
7984
}
8085

@@ -83,41 +88,54 @@ assigns_clauset::conditional_address_ranget::generate_snapshot_instructions()
8388
const
8489
{
8590
goto_programt instructions;
86-
87-
instructions.add(goto_programt::make_decl(validity_condition_var, location));
88-
instructions.add(goto_programt::make_decl(lower_bound_address_var, location));
89-
instructions.add(goto_programt::make_decl(upper_bound_address_var, location));
91+
// adding pragmas to the location to selectively disable checks
92+
// where it is sound to do so
93+
source_locationt location_no_checks = location;
94+
disable_pointer_checks(location_no_checks);
95+
96+
instructions.add(
97+
goto_programt::make_decl(validity_condition_var, location_no_checks));
98+
instructions.add(
99+
goto_programt::make_decl(lower_bound_address_var, location_no_checks));
100+
instructions.add(
101+
goto_programt::make_decl(upper_bound_address_var, location_no_checks));
90102

91103
instructions.add(goto_programt::make_assignment(
92104
lower_bound_address_var,
93105
null_pointer_exprt{to_pointer_type(slice.first.type())},
94-
location));
106+
location_no_checks));
95107
instructions.add(goto_programt::make_assignment(
96108
upper_bound_address_var,
97109
null_pointer_exprt{to_pointer_type(slice.first.type())},
98-
location));
110+
location_no_checks));
99111

100112
goto_programt skip_program;
101-
const auto skip_target = skip_program.add(goto_programt::make_skip(location));
113+
const auto skip_target =
114+
skip_program.add(goto_programt::make_skip(location_no_checks));
102115

103116
const auto validity_check_expr =
104117
and_exprt{all_dereferences_are_valid(source_expr, parent.ns),
105118
w_ok_exprt{slice.first, slice.second}};
106119
instructions.add(goto_programt::make_assignment(
107-
validity_condition_var, validity_check_expr, location));
120+
validity_condition_var, validity_check_expr, location_no_checks));
108121

109122
instructions.add(goto_programt::make_goto(
110-
skip_target, not_exprt{validity_condition_var}, location));
123+
skip_target, not_exprt{validity_condition_var}, location_no_checks));
111124

112125
instructions.add(goto_programt::make_assignment(
113-
lower_bound_address_var, slice.first, location));
126+
lower_bound_address_var, slice.first, location_no_checks));
127+
128+
// the computation of the CAR upper bound can overflow into the object ID bits
129+
// of the pointer with very large allocation sizes.
130+
// We enable pointer overflow checks to detect such cases.
131+
source_locationt location_overflow_check = location;
132+
location_overflow_check.add_pragma("enable:pointer-overflow-check");
114133

115134
instructions.add(goto_programt::make_assignment(
116135
upper_bound_address_var,
117136
minus_exprt{plus_exprt{slice.first, slice.second},
118137
from_integer(1, slice.second.type())},
119-
location));
120-
138+
location_overflow_check));
121139
instructions.destructive_append(skip_program);
122140

123141
// The assignments above are only performed on locally defined temporaries
@@ -130,14 +148,16 @@ const exprt
130148
assigns_clauset::conditional_address_ranget::generate_unsafe_inclusion_check(
131149
const conditional_address_ranget &lhs) const
132150
{
151+
// remark: both lb and ub are derived from the same object so checking
152+
// same_object(upper_bound_address_var, lhs.upper_bound_address_var)
153+
// is redundant
133154
return conjunction(
134155
{validity_condition_var,
135156
same_object(lower_bound_address_var, lhs.lower_bound_address_var),
136-
same_object(lhs.upper_bound_address_var, upper_bound_address_var),
137-
less_than_or_equal_exprt{lower_bound_address_var,
138-
lhs.lower_bound_address_var},
139-
less_than_or_equal_exprt{lhs.upper_bound_address_var,
140-
upper_bound_address_var}});
157+
less_than_or_equal_exprt{pointer_offset(lower_bound_address_var),
158+
pointer_offset(lhs.lower_bound_address_var)},
159+
less_than_or_equal_exprt{pointer_offset(lhs.upper_bound_address_var),
160+
pointer_offset(upper_bound_address_var)}});
141161
}
142162

143163
assigns_clauset::assigns_clauset(

src/goto-instrument/contracts/assigns.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,10 @@ class assigns_clauset
6060
const exprt
6161
generate_unsafe_inclusion_check(const conditional_address_ranget &) const;
6262

63-
const symbolt
64-
generate_new_symbol(const typet &, const source_locationt &) const;
63+
const symbolt generate_new_symbol(
64+
const std::string &prefix,
65+
const typet &,
66+
const source_locationt &) const;
6567

6668
friend class assigns_clauset;
6769
};

src/goto-instrument/contracts/contracts.cpp

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -911,6 +911,8 @@ void code_contractst::instrument_call_statement(
911911
}
912912
else if(callee_name == "free")
913913
{
914+
source_locationt location_no_checks = instruction_it->source_location();
915+
disable_pointer_checks(location_no_checks);
914916
const auto free_car = add_inclusion_check(
915917
body,
916918
assigns,
@@ -920,10 +922,9 @@ void code_contractst::instrument_call_statement(
920922
// skip all invalidation business if we're freeing invalid memory
921923
goto_programt alias_checking_instructions, skip_program;
922924
alias_checking_instructions.add(goto_programt::make_goto(
923-
skip_program.add(
924-
goto_programt::make_skip(instruction_it->source_location())),
925+
skip_program.add(goto_programt::make_skip(location_no_checks)),
925926
not_exprt{free_car.validity_condition_var},
926-
instruction_it->source_location()));
927+
location_no_checks));
927928

928929
// Since the argument to free may be an "alias" (but not identical)
929930
// to existing CARs' source_expr, structural equality wouldn't work.
@@ -936,14 +937,15 @@ void code_contractst::instrument_call_statement(
936937
const auto object_validity_var_addr =
937938
new_tmp_symbol(
938939
pointer_type(bool_typet{}),
939-
instruction_it->source_location(),
940+
location_no_checks,
940941
symbol_table.lookup_ref(function).mode,
941-
symbol_table)
942+
symbol_table,
943+
"__car_valid")
942944
.symbol_expr();
943945
write_set_validity_addrs.insert(object_validity_var_addr);
944946

945-
alias_checking_instructions.add(goto_programt::make_decl(
946-
object_validity_var_addr, instruction_it->source_location()));
947+
alias_checking_instructions.add(
948+
goto_programt::make_decl(object_validity_var_addr, location_no_checks));
947949
// if the CAR was defined on the same_object as the one being `free`d,
948950
// record its validity variable's address, otherwise record NULL
949951
alias_checking_instructions.add(goto_programt::make_assignment(
@@ -955,7 +957,7 @@ void code_contractst::instrument_call_statement(
955957
free_car.lower_bound_address_var, w_car.lower_bound_address_var)},
956958
address_of_exprt{w_car.validity_condition_var},
957959
null_pointer_exprt{to_pointer_type(object_validity_var_addr.type())}},
958-
instruction_it->source_location()));
960+
location_no_checks));
959961
}
960962

961963
alias_checking_instructions.destructive_append(skip_program);
@@ -971,24 +973,22 @@ void code_contractst::instrument_call_statement(
971973
goto_programt invalidation_instructions;
972974
skip_program.clear();
973975
invalidation_instructions.add(goto_programt::make_goto(
974-
skip_program.add(
975-
goto_programt::make_skip(instruction_it->source_location())),
976+
skip_program.add(goto_programt::make_skip(location_no_checks)),
976977
not_exprt{free_car.validity_condition_var},
977-
instruction_it->source_location()));
978+
location_no_checks));
978979

979980
// invalidate all recorded CAR validity variables
980981
for(const auto &w_car_validity_addr : write_set_validity_addrs)
981982
{
982983
goto_programt w_car_skip_program;
983984
invalidation_instructions.add(goto_programt::make_goto(
984-
w_car_skip_program.add(
985-
goto_programt::make_skip(instruction_it->source_location())),
985+
w_car_skip_program.add(goto_programt::make_skip(location_no_checks)),
986986
null_pointer(w_car_validity_addr),
987-
instruction_it->source_location()));
987+
location_no_checks));
988988
invalidation_instructions.add(goto_programt::make_assignment(
989989
dereference_exprt{w_car_validity_addr},
990990
false_exprt{},
991-
instruction_it->source_location()));
991+
location_no_checks));
992992
invalidation_instructions.destructive_append(w_car_skip_program);
993993
}
994994

@@ -1174,6 +1174,9 @@ void code_contractst::check_frame_conditions(
11741174
else if(instruction_it->is_dead())
11751175
{
11761176
const auto &symbol = instruction_it->dead_symbol();
1177+
source_locationt location_no_checks = instruction_it->source_location();
1178+
disable_pointer_checks(location_no_checks);
1179+
11771180
// CAR equality and hash are defined on source_expr alone,
11781181
// therefore this temporary CAR should be "found"
11791182
const auto &symbol_car = assigns.get_write_set().find(
@@ -1227,10 +1230,13 @@ code_contractst::add_inclusion_check(
12271230
program, instruction_it, snapshot_instructions);
12281231

12291232
goto_programt assertion;
1230-
assertion.add(goto_programt::make_assertion(
1231-
assigns.generate_inclusion_check(car), instruction_it->source_location()));
1232-
assertion.instructions.back().source_location_nonconst().set_comment(
1233+
source_locationt location_no_checks =
1234+
instruction_it->source_location_nonconst();
1235+
disable_pointer_checks(location_no_checks);
1236+
location_no_checks.set_comment(
12331237
"Check that " + from_expr(ns, expr.id(), expr) + " is assignable");
1238+
assertion.add(goto_programt::make_assertion(
1239+
assigns.generate_inclusion_check(car), location_no_checks));
12341240
insert_before_swap_and_advance(program, instruction_it, assertion);
12351241

12361242
return car;

src/goto-instrument/contracts/utils.cpp

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -142,13 +142,24 @@ const symbolt &new_tmp_symbol(
142142
const typet &type,
143143
const source_locationt &location,
144144
const irep_idt &mode,
145-
symbol_table_baset &symtab)
145+
symbol_table_baset &symtab,
146+
std::string suffix,
147+
bool is_auxiliary)
146148
{
147-
return get_fresh_aux_symbol(
149+
symbolt &new_symbol = get_fresh_aux_symbol(
148150
type,
149-
id2string(location.get_function()) + "::tmp_cc",
150-
"tmp_cc",
151+
id2string(location.get_function()) + "::",
152+
suffix,
151153
location,
152154
mode,
153155
symtab);
156+
new_symbol.is_auxiliary = is_auxiliary;
157+
return new_symbol;
158+
}
159+
160+
void disable_pointer_checks(source_locationt &source_location)
161+
{
162+
source_location.add_pragma("disable:pointer-check");
163+
source_location.add_pragma("disable:pointer-primitive-check");
164+
source_location.add_pragma("disable:pointer-overflow-check");
154165
}

src/goto-instrument/contracts/utils.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,17 +105,24 @@ void insert_before_swap_and_advance(
105105
goto_programt::targett &target,
106106
goto_programt &payload);
107107

108-
/// \brief Adds a new temporary symbol to the symbol table.
108+
/// \brief Adds a fresh and uniquely named symbol to the symbol table.
109109
///
110110
/// \param type: The type of the new symbol.
111111
/// \param location: The source location for the new symbol.
112112
/// \param mode: The mode for the new symbol, e.g. ID_C, ID_java.
113113
/// \param symtab: The symbol table to which the new symbol is to be added.
114+
/// \param suffix: Suffix to use to generate the unique name
115+
/// \param is_auxiliary: Do not print symbol in traces if true (default = false)
114116
/// \return The new symbolt object.
115117
const symbolt &new_tmp_symbol(
116118
const typet &type,
117119
const source_locationt &location,
118120
const irep_idt &mode,
119-
symbol_table_baset &symtab);
121+
symbol_table_baset &symtab,
122+
std::string suffix = "tmp_cc",
123+
bool is_auxiliary = false);
124+
125+
/// Add disable pragmas for all pointer checks on the given location
126+
void disable_pointer_checks(source_locationt &source_location);
120127

121128
#endif // CPROVER_GOTO_INSTRUMENT_CONTRACTS_UTILS_H

0 commit comments

Comments
 (0)