Skip to content

Commit 859d74c

Browse files
Class for string builtin functions and dependences
String builtin functions can be initialized from function_application_exprt for some function names, for now only cprover_string_concat_func and cprover_string_insert_func are supported but this could be extended to all builtin functoins supported by the solver. We use information of string argument and return values of the functions for creating a dependency graph between the strings, stored in the string dependences structure. We also define indexes in the dependency graph so that we can reuse generic algorithms of util/graph.
1 parent bd8ee51 commit 859d74c

File tree

3 files changed

+498
-2
lines changed

3 files changed

+498
-2
lines changed

src/solvers/refinement/string_refinement.cpp

+27-2
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,23 @@ static void substitute_function_applications_in_equations(
287287
substitute_function_applications(eq.rhs(), generator);
288288
}
289289

290+
/// Fill the array_pointer correspondence and replace the right hand sides of
291+
/// the corresponding equations
292+
static void make_char_array_pointer_associations(
293+
string_constraint_generatort &generator,
294+
std::vector<equal_exprt> &equations)
295+
{
296+
for(equal_exprt &eq : equations)
297+
{
298+
if(
299+
const auto fun_app =
300+
expr_try_dynamic_cast<function_application_exprt>(eq.rhs()))
301+
{
302+
if(const auto result = generator.make_array_pointer_association(*fun_app))
303+
eq.rhs() = *result;
304+
}
305+
}
306+
}
290307

291308
void replace_symbols_in_equations(
292309
const union_find_replacet &symbol_resolve,
@@ -658,8 +675,17 @@ decision_proceduret::resultt string_refinementt::dec_solve()
658675
string_id_symbol_resolve.replace_expr(eq.rhs());
659676
}
660677

678+
make_char_array_pointer_associations(generator, equations);
679+
661680
#ifdef DEBUG
662681
output_equations(debug(), equations, ns);
682+
683+
string_dependencest dependences;
684+
for(const equal_exprt &eq : equations)
685+
add_node(dependences, eq, generator.array_pool);
686+
687+
debug() << "dec_solve: dependence graph:" << eom;
688+
dependences.output_dot(debug());
663689
#endif
664690

665691
debug() << "dec_solve: Replace function applications" << eom;
@@ -671,8 +697,7 @@ decision_proceduret::resultt string_refinementt::dec_solve()
671697

672698
#ifdef DEBUG
673699
debug() << "dec_solve: arrays_of_pointers:" << eom;
674-
for(auto pair :
675-
generator.array_pointer_correspondance.get_arrays_of_pointers())
700+
for(auto pair : generator.array_pool.get_arrays_of_pointers())
676701
{
677702
debug() << " * " << format(pair.first) << "\t--> " << format(pair.second)
678703
<< " : " << format(pair.second.type()) << eom;

src/solvers/refinement/string_refinement_util.cpp

+271
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,13 @@
88

99
#include <algorithm>
1010
#include <numeric>
11+
#include <functional>
12+
#include <iostream>
1113
#include <util/arith_tools.h>
14+
#include <util/ssa_expr.h>
1215
#include <util/std_expr.h>
1316
#include <util/expr_iterator.h>
17+
#include <util/graph.h>
1418
#include <util/magic.h>
1519
#include "string_refinement_util.h"
1620

@@ -157,3 +161,270 @@ equation_symbol_mappingt::find_equations(const exprt &expr)
157161
{
158162
return equations_containing[expr];
159163
}
164+
165+
string_insertion_builtin_functiont::string_insertion_builtin_functiont(
166+
const std::vector<exprt> &fun_args,
167+
array_poolt &array_pool)
168+
{
169+
PRECONDITION(fun_args.size() > 3);
170+
const auto arg1 = expr_checked_cast<struct_exprt>(fun_args[2]);
171+
input1 = array_pool.find(arg1.op1(), arg1.op0());
172+
const auto arg2 = expr_checked_cast<struct_exprt>(fun_args[3]);
173+
input2 = array_pool.find(arg2.op1(), arg2.op0());
174+
result = array_pool.find(fun_args[1], fun_args[0]);
175+
args.insert(args.end(), fun_args.begin() + 4, fun_args.end());
176+
}
177+
178+
/// Construct a string_builtin_functiont object from a function application
179+
/// \return a unique pointer to the created object, this unique pointer is empty
180+
/// if the function does not correspond to one of the supported
181+
/// builtin_functions.
182+
static std::unique_ptr<string_builtin_functiont> to_string_builtin_function(
183+
const function_application_exprt &fun_app,
184+
array_poolt &array_pool)
185+
{
186+
const auto name = expr_checked_cast<symbol_exprt>(fun_app.function());
187+
const irep_idt &id = is_ssa_expr(name) ? to_ssa_expr(name).get_object_name()
188+
: name.get_identifier();
189+
190+
if(id == ID_cprover_string_insert_func)
191+
return std::unique_ptr<string_builtin_functiont>(
192+
new string_insertion_builtin_functiont(fun_app.arguments(), array_pool));
193+
194+
if(id == ID_cprover_string_concat_func)
195+
return std::unique_ptr<string_builtin_functiont>(
196+
new string_insertion_builtin_functiont(fun_app.arguments(), array_pool));
197+
198+
return {};
199+
}
200+
201+
string_dependencest::string_nodet &
202+
string_dependencest::get_node(const array_string_exprt &e)
203+
{
204+
auto entry_inserted = node_index_pool.emplace(e, string_nodes.size());
205+
if(!entry_inserted.second)
206+
return string_nodes[entry_inserted.first->second];
207+
208+
string_nodes.emplace_back();
209+
return string_nodes.back();
210+
}
211+
212+
string_dependencest::builtin_function_nodet string_dependencest::make_node(
213+
std::unique_ptr<string_builtin_functiont> &builtin_function)
214+
{
215+
const builtin_function_nodet builtin_function_node(
216+
builtin_function_nodes.size());
217+
builtin_function_nodes.emplace_back();
218+
builtin_function_nodes.back().swap(builtin_function);
219+
return builtin_function_node;
220+
}
221+
222+
const string_builtin_functiont &string_dependencest::get_builtin_function(
223+
const builtin_function_nodet &node) const
224+
{
225+
PRECONDITION(node.index < builtin_function_nodes.size());
226+
return *(builtin_function_nodes[node.index]);
227+
}
228+
229+
const std::vector<string_dependencest::builtin_function_nodet> &
230+
string_dependencest::dependencies(
231+
const string_dependencest::string_nodet &node) const
232+
{
233+
return node.dependencies;
234+
}
235+
236+
void string_dependencest::add_dependency(
237+
const array_string_exprt &e,
238+
const builtin_function_nodet &builtin_function_node)
239+
{
240+
string_nodet &string_node = get_node(e);
241+
string_node.dependencies.push_back(builtin_function_node);
242+
}
243+
244+
void string_dependencest::add_unknown_dependency(const array_string_exprt &e)
245+
{
246+
string_nodet &string_node = get_node(e);
247+
string_node.depends_on_unknown_builtin_function = true;
248+
}
249+
250+
static void add_unknown_dependency_to_string_subexprs(
251+
string_dependencest &dependencies,
252+
const function_application_exprt &fun_app,
253+
array_poolt &array_pool)
254+
{
255+
for(const auto &expr : fun_app.arguments())
256+
{
257+
std::for_each(
258+
expr.depth_begin(), expr.depth_end(), [&](const exprt &e) { // NOLINT
259+
const auto &string_struct = expr_try_dynamic_cast<struct_exprt>(e);
260+
if(string_struct && string_struct->operands().size() == 2)
261+
{
262+
const array_string_exprt string =
263+
array_pool.find(string_struct->op1(), string_struct->op0());
264+
dependencies.add_unknown_dependency(string);
265+
}
266+
});
267+
}
268+
}
269+
270+
static void add_dependency_to_string_subexprs(
271+
string_dependencest &dependencies,
272+
const function_application_exprt &fun_app,
273+
const string_dependencest::builtin_function_nodet &builtin_function_node,
274+
array_poolt &array_pool)
275+
{
276+
PRECONDITION(fun_app.arguments()[0].type().id() != ID_pointer);
277+
if(
278+
fun_app.arguments().size() > 1 &&
279+
fun_app.arguments()[1].type().id() == ID_pointer)
280+
{
281+
// Case where the result is given as a pointer argument
282+
const array_string_exprt string =
283+
array_pool.find(fun_app.arguments()[1], fun_app.arguments()[0]);
284+
dependencies.add_dependency(string, builtin_function_node);
285+
}
286+
287+
for(const auto &expr : fun_app.arguments())
288+
{
289+
std::for_each(
290+
expr.depth_begin(),
291+
expr.depth_end(),
292+
[&](const exprt &e) { // NOLINT
293+
if(const auto structure = expr_try_dynamic_cast<struct_exprt>(e))
294+
{
295+
const array_string_exprt string = array_pool.of_argument(*structure);
296+
dependencies.add_dependency(string, builtin_function_node);
297+
}
298+
});
299+
}
300+
}
301+
302+
string_dependencest::node_indext string_dependencest::size() const
303+
{
304+
return builtin_function_nodes.size() + string_nodes.size();
305+
}
306+
307+
/// Convert an index of a string node in `string_nodes` to the node_indext for
308+
/// the same node
309+
static std::size_t string_index_to_node_index(const std::size_t string_index)
310+
{
311+
return 2 * string_index + 1;
312+
}
313+
314+
/// Convert an index of a builtin function node to the node_indext for
315+
/// the same node
316+
static std::size_t
317+
builtin_function_index_to_node_index(const std::size_t builtin_index)
318+
{
319+
return 2 * builtin_index;
320+
}
321+
322+
string_dependencest::node_indext
323+
string_dependencest::node_index(const builtin_function_nodet &n) const
324+
{
325+
return builtin_function_index_to_node_index(n.index);
326+
}
327+
328+
string_dependencest::node_indext
329+
string_dependencest::node_index(const array_string_exprt &s) const
330+
{
331+
return string_index_to_node_index(node_index_pool.at(s));
332+
}
333+
334+
optionalt<string_dependencest::builtin_function_nodet>
335+
string_dependencest::get_builtin_function_node(node_indext i) const
336+
{
337+
if(i % 2 == 0)
338+
return builtin_function_nodet(i / 2);
339+
return {};
340+
}
341+
342+
optionalt<string_dependencest::string_nodet>
343+
string_dependencest::get_string_node(node_indext i) const
344+
{
345+
if(i % 2 == 1 && i / 2 < string_nodes.size())
346+
return string_nodes[i / 2];
347+
return {};
348+
}
349+
350+
bool add_node(
351+
string_dependencest &dependencies,
352+
const equal_exprt &equation,
353+
array_poolt &array_pool)
354+
{
355+
const auto fun_app =
356+
expr_try_dynamic_cast<function_application_exprt>(equation.rhs());
357+
if(!fun_app)
358+
return false;
359+
360+
auto builtin_function = to_string_builtin_function(*fun_app, array_pool);
361+
362+
if(!builtin_function)
363+
{
364+
add_unknown_dependency_to_string_subexprs(
365+
dependencies, *fun_app, array_pool);
366+
return true;
367+
}
368+
369+
const auto &builtin_function_node = dependencies.make_node(builtin_function);
370+
// Warning: `builtin_function` has been emptied and should not be used anymore
371+
372+
if(
373+
const auto &string_result =
374+
dependencies.get_builtin_function(builtin_function_node).string_result())
375+
dependencies.add_dependency(*string_result, builtin_function_node);
376+
else
377+
add_dependency_to_string_subexprs(
378+
dependencies, *fun_app, builtin_function_node, array_pool);
379+
380+
return true;
381+
}
382+
383+
void string_dependencest::for_each_successor(
384+
const std::size_t &i,
385+
const std::function<void(const std::size_t &)> &f) const
386+
{
387+
if(const auto &builtin_function_node = get_builtin_function_node(i))
388+
{
389+
const string_builtin_functiont &p =
390+
get_builtin_function(*builtin_function_node);
391+
std::for_each(
392+
p.string_arguments().begin(),
393+
p.string_arguments().end(),
394+
[&](const array_string_exprt &s) { f(node_index(s)); });
395+
}
396+
else if(const auto &s = get_string_node(i))
397+
{
398+
std::for_each(
399+
s->dependencies.begin(),
400+
s->dependencies.end(),
401+
[&](const builtin_function_nodet &p) { f(node_index(p)); });
402+
}
403+
else
404+
UNREACHABLE;
405+
}
406+
407+
void string_dependencest::output_dot(std::ostream &stream) const
408+
{
409+
const auto for_each_node =
410+
[&](const std::function<void(const std::size_t &)> &f) { // NOLINT
411+
for(std::size_t i = 0; i < string_nodes.size(); ++i)
412+
f(string_index_to_node_index(i));
413+
for(std::size_t i = 0; i < builtin_function_nodes.size(); ++i)
414+
f(builtin_function_index_to_node_index(i));
415+
};
416+
417+
const auto for_each_succ = [&](
418+
const std::size_t &i,
419+
const std::function<void(const std::size_t &)> &f) { // NOLINT
420+
for_each_successor(i, f);
421+
};
422+
423+
const auto node_to_string = [&](const std::size_t &i) { // NOLINT
424+
return std::to_string(i);
425+
};
426+
stream << "digraph dependencies {\n";
427+
output_dot_generic<std::size_t>(
428+
stream, for_each_node, for_each_succ, node_to_string);
429+
stream << '}' << std::endl;
430+
}

0 commit comments

Comments
 (0)