Skip to content

Commit d583cae

Browse files
committed
Replace function calls
Adds an option --replace-calls f:g (can be repeated) to goto-instrument which replaces calls to function f by calls to function g. A use case is to replace certain functions by simpler stubs for analysis.
1 parent 7ffb27d commit d583cae

File tree

18 files changed

+396
-0
lines changed

18 files changed

+396
-0
lines changed
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
int f(int a)
2+
{
3+
return a;
4+
}
5+
6+
int g(int b)
7+
{
8+
return b + 1;
9+
}
10+
11+
int main()
12+
{
13+
int r;
14+
15+
r = f(0);
16+
assert(r == 1);
17+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
CORE
2+
main.c
3+
--replace-calls f:g
4+
g\(0\);
5+
^VERIFICATION SUCCESSFUL$
6+
^EXIT=0$
7+
^SIGNAL=0$
8+
--
9+
f\(0\);
10+
^warning: ignoring
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
int f()
2+
{
3+
return 0;
4+
}
5+
6+
int g()
7+
{
8+
return 1;
9+
}
10+
11+
int h()
12+
{
13+
return 2;
14+
}
15+
16+
int i()
17+
{
18+
return 3;
19+
}
20+
21+
int main()
22+
{
23+
int r;
24+
25+
r = f();
26+
assert(r == 1);
27+
28+
r = h();
29+
assert(r == 3);
30+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
CORE
2+
main.c
3+
--replace-calls f:g --replace-calls h:i
4+
g\(\);
5+
i\(\);
6+
^VERIFICATION SUCCESSFUL$
7+
^EXIT=0$
8+
^SIGNAL=0$
9+
--
10+
f\(\);
11+
h\(\);
12+
^warning: ignoring
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
int f()
2+
{
3+
return 0;
4+
}
5+
6+
char g()
7+
{
8+
return 1;
9+
}
10+
11+
int main()
12+
{
13+
f();
14+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CORE
2+
main.c
3+
--replace-calls f:g
4+
Functions f and g are not type-compatible
5+
^EXIT=11$
6+
^SIGNAL=0$
7+
--
8+
^warning: ignoring
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
void f(int a)
2+
{
3+
return 0;
4+
}
5+
6+
void g(unsigned a)
7+
{
8+
return 1;
9+
}
10+
11+
int main()
12+
{
13+
f(0);
14+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CORE
2+
main.c
3+
--replace-calls f:g
4+
Functions f and g are not type-compatible
5+
^EXIT=11$
6+
^SIGNAL=0$
7+
--
8+
^warning: ignoring
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
void g()
2+
{
3+
4+
}
5+
6+
int main()
7+
{
8+
return 0;
9+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CORE
2+
main.c
3+
--replace-calls f:g --replace-calls h:f
4+
Function f cannot both be replaced and be a replacement
5+
^EXIT=11$
6+
^SIGNAL=0$
7+
--
8+
^warning: ignoring
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
int f()
2+
{
3+
return 0;
4+
}
5+
6+
int g()
7+
{
8+
return 1;
9+
}
10+
11+
int main()
12+
{
13+
int (*h)(void);
14+
15+
h = g;
16+
h = f;
17+
18+
int r;
19+
r = h();
20+
assert(r == 1);
21+
22+
return 0;
23+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
CORE
2+
main.c
3+
--replace-calls f:g
4+
^VERIFICATION SUCCESSFUL$
5+
^EXIT=0$
6+
^SIGNAL=0$
7+
--
8+
f\(\);
9+
^warning: ignoring

src/cbmc/cbmc_parse_options.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ Author: Daniel Kroening, [email protected]
2121
#include <analyses/goto_check.h>
2222

2323
#include <goto-programs/goto_trace.h>
24+
#include <goto-programs/replace_calls.h>
2425

2526
#include "bmc.h"
2627
#include "xml_interface.h"

src/goto-instrument/goto_instrument_parse_options.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1063,6 +1063,14 @@ void goto_instrument_parse_optionst::instrument_goto_program()
10631063
do_remove_const_function_pointers_only();
10641064
}
10651065

1066+
if(cmdline.isset("replace-calls"))
1067+
{
1068+
do_indirect_call_and_rtti_removal();
1069+
1070+
replace_callst replace_calls;
1071+
replace_calls(goto_model, cmdline.get_values("replace-calls"));
1072+
}
1073+
10661074
if(cmdline.isset("function-inline"))
10671075
{
10681076
std::string function=cmdline.get_value("function-inline");
@@ -1628,6 +1636,7 @@ void goto_instrument_parse_optionst::help()
16281636
" --model-argc-argv <n> model up to <n> command line arguments\n"
16291637
// NOLINTNEXTLINE(whitespace/line_length)
16301638
" --remove-function-body <f> remove the implementation of function <f> (may be repeated)\n"
1639+
HELP_REPLACE_CALLS
16311640
"\n"
16321641
"Other options:\n"
16331642
" --no-system-headers with --dump-c/--dump-cpp: generate C source expanding libc includes\n" // NOLINT(*)

src/goto-instrument/goto_instrument_parse_options.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ Author: Daniel Kroening, [email protected]
2020
#include <goto-programs/goto_functions.h>
2121
#include <goto-programs/remove_calls_no_body.h>
2222
#include <goto-programs/remove_const_function_pointers.h>
23+
#include <goto-programs/replace_calls.h>
2324
#include <goto-programs/show_goto_functions.h>
2425
#include <goto-programs/show_properties.h>
2526

@@ -98,6 +99,7 @@ Author: Daniel Kroening, [email protected]
9899
OPT_REPLACE_FUNCTION_BODY \
99100
OPT_GOTO_PROGRAM_STATS \
100101
"(show-local-safe-pointers)(show-safe-dereferences)" \
102+
OPT_REPLACE_CALLS \
101103
// empty last line
102104

103105
// clang-format on

src/goto-programs/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ SRC = adjust_float_expressions.cpp \
5151
remove_unused_functions.cpp \
5252
remove_vector.cpp \
5353
remove_virtual_functions.cpp \
54+
replace_calls.cpp \
5455
resolve_inherited_component.cpp \
5556
safety_checker.cpp \
5657
set_properties.cpp \

src/goto-programs/replace_calls.cpp

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
/*******************************************************************\
2+
3+
Module: Replace calls
4+
5+
Author: Daniel Poetzl
6+
7+
\*******************************************************************/
8+
9+
/// \file
10+
/// Replace calls to given functions with calls to other given
11+
/// functions
12+
13+
#include "replace_calls.h"
14+
15+
#include <goto-programs/remove_returns.h>
16+
17+
#include <util/base_type.h>
18+
#include <util/invariant.h>
19+
#include <util/irep.h>
20+
#include <util/string_utils.h>
21+
#include <util/suffix.h>
22+
23+
/// Replace function calls with calls to other functions
24+
/// \param goto_model: goto model to modify
25+
/// \param replacement_list: list of strings, with each string f:g denoting a
26+
/// mapping between functions names; a mapping f -> g indicates that calls to
27+
/// f should be replaced by calls to g
28+
void replace_callst::operator()(
29+
goto_modelt &goto_model,
30+
const replacement_listt &replacement_list) const
31+
{
32+
replacement_mapt replacement_map = parse_replacement_list(replacement_list);
33+
(*this)(goto_model, replacement_map);
34+
}
35+
36+
/// Replace function calls with calls to other functions
37+
/// \param goto_model: goto model to modify
38+
/// \param replacement_map: mapping between function names; a mapping f -> g
39+
/// indicates that calls to f should be replaced by calls to g
40+
void replace_callst::operator()(
41+
goto_modelt &goto_model,
42+
const replacement_mapt &replacement_map) const
43+
{
44+
const namespacet ns(goto_model.symbol_table);
45+
goto_functionst &goto_functions = goto_model.goto_functions;
46+
47+
check_replacement_map(replacement_map, goto_functions, ns);
48+
49+
for(auto &p : goto_functions.function_map)
50+
{
51+
goto_functionst::goto_functiont &goto_function = p.second;
52+
goto_programt &goto_program = goto_function.body;
53+
54+
(*this)(goto_program, goto_functions, ns, replacement_map);
55+
}
56+
}
57+
58+
void replace_callst::operator()(
59+
goto_programt &goto_program,
60+
const goto_functionst &goto_functions,
61+
const namespacet &ns,
62+
const replacement_mapt &replacement_map) const
63+
{
64+
Forall_goto_program_instructions(it, goto_program)
65+
{
66+
goto_programt::instructiont &ins = *it;
67+
68+
if(!ins.is_function_call())
69+
continue;
70+
71+
code_function_callt &cfc = to_code_function_call(ins.code);
72+
exprt &function = cfc.function();
73+
74+
PRECONDITION(function.id() == ID_symbol);
75+
76+
symbol_exprt &se = to_symbol_expr(function);
77+
const irep_idt &id = se.get_identifier();
78+
79+
auto f_it1 = goto_functions.function_map.find(id);
80+
81+
DATA_INVARIANT(
82+
f_it1 != goto_functions.function_map.end(),
83+
"Called functions need to be present");
84+
85+
replacement_mapt::const_iterator r_it = replacement_map.find(id);
86+
87+
if(r_it == replacement_map.end())
88+
continue;
89+
90+
const irep_idt &new_id = r_it->second;
91+
92+
auto f_it2 = goto_functions.function_map.find(new_id);
93+
PRECONDITION(f_it2 != goto_functions.function_map.end());
94+
95+
PRECONDITION(
96+
base_type_eq(f_it1->second.type, f_it2->second.type, ns));
97+
98+
// check that returns have not been removed
99+
goto_programt::const_targett next_it = std::next(it);
100+
if(next_it != goto_program.instructions.end() && next_it->is_assign())
101+
{
102+
const code_assignt &ca = to_code_assign(next_it->code);
103+
const exprt &rhs = ca.rhs();
104+
105+
if(rhs.id() == ID_symbol)
106+
{
107+
const symbol_exprt &se = to_symbol_expr(rhs);
108+
if(has_suffix(id2string(se.get_identifier()), RETURN_VALUE_SUFFIX))
109+
throw "Returns must not be removed before replacing calls";
110+
}
111+
}
112+
113+
// Finally modify the call
114+
function.type() = f_it2->second.type;
115+
se.set_identifier(new_id);
116+
}
117+
}
118+
119+
replace_callst::replacement_mapt replace_callst::parse_replacement_list(
120+
const replacement_listt &replacement_list) const
121+
{
122+
replacement_mapt replacement_map;
123+
124+
for(const std::string &s : replacement_list)
125+
{
126+
std::string original;
127+
std::string replacement;
128+
129+
split_string(s, ':', original, replacement, true);
130+
131+
const auto r =
132+
replacement_map.insert(std::make_pair(original, replacement));
133+
134+
if(!r.second)
135+
throw "Conflicting replacement for function " + original;
136+
}
137+
138+
return replacement_map;
139+
}
140+
141+
void replace_callst::check_replacement_map(
142+
const replacement_mapt &replacement_map,
143+
const goto_functionst &goto_functions,
144+
const namespacet &ns) const
145+
{
146+
for(const auto &p : replacement_map)
147+
{
148+
if(replacement_map.find(p.second) != replacement_map.end())
149+
throw "Function " + id2string(p.second) +
150+
" cannot both be replaced and "
151+
"be a replacement";
152+
153+
auto it2 = goto_functions.function_map.find(p.second);
154+
155+
if(it2 == goto_functions.function_map.end())
156+
throw "Replacement function " + id2string(p.second) +
157+
" needs to be present";
158+
159+
160+
auto it1 = goto_functions.function_map.find(p.first);
161+
if(it1 != goto_functions.function_map.end())
162+
{
163+
if(!base_type_eq(it1->second.type, it2->second.type, ns))
164+
throw "Functions " + id2string(p.first) + " and " +
165+
id2string(p.second) + " are not type-compatible";
166+
}
167+
}
168+
}

0 commit comments

Comments
 (0)