Skip to content

Commit 627a706

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 5ff4004 commit 627a706

File tree

3 files changed

+498
-2
lines changed

3 files changed

+498
-2
lines changed

src/solvers/refinement/string_refinement.cpp

Lines changed: 27 additions & 2 deletions
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

Lines changed: 271 additions & 0 deletions
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

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

0 commit comments

Comments
 (0)