Skip to content

Commit a0cb233

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 ceb97c9 commit a0cb233

File tree

2 files changed

+445
-0
lines changed

2 files changed

+445
-0
lines changed

src/solvers/refinement/string_refinement_util.cpp

Lines changed: 243 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 "string_refinement_util.h"
1519

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

0 commit comments

Comments
 (0)