Skip to content

Commit 06dc6b2

Browse files
Add an evaluation method to builtin functions
This will be used to concretize strings by directly computing them from the primitives instead of using the solver, when possible.
1 parent f6a153e commit 06dc6b2

File tree

2 files changed

+155
-2
lines changed

2 files changed

+155
-2
lines changed

src/solvers/refinement/string_refinement_util.cpp

+126-2
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,19 @@
1616
#include <util/expr_iterator.h>
1717
#include <util/graph.h>
1818
#include <util/magic.h>
19+
#include <util/make_unique.h>
1920
#include "string_refinement_util.h"
2021

22+
/// Get the valuation of the string, given a valuation
23+
static optionalt<std::vector<mp_integer>> eval_string(
24+
const array_string_exprt &a,
25+
const std::function<exprt(const exprt &)> &get_value);
26+
27+
/// Make a string from a constant array
28+
static array_string_exprt make_string(
29+
const std::vector<mp_integer> &array,
30+
const array_typet &array_type);
31+
2132
bool is_char_type(const typet &type)
2233
{
2334
return type.id() == ID_unsignedbv &&
@@ -175,6 +186,119 @@ string_insertion_builtin_functiont::string_insertion_builtin_functiont(
175186
args.insert(args.end(), fun_args.begin() + 4, fun_args.end());
176187
}
177188

189+
optionalt<std::vector<mp_integer>> eval_string(
190+
const array_string_exprt &a,
191+
const std::function<exprt(const exprt &)> &get_value)
192+
{
193+
if(a.id() == ID_if)
194+
{
195+
const if_exprt &ite = to_if_expr(a);
196+
const exprt cond = get_value(ite.cond());
197+
if(!cond.is_constant())
198+
return {};
199+
return cond.is_true()
200+
? eval_string(to_array_string_expr(ite.true_case()), get_value)
201+
: eval_string(to_array_string_expr(ite.false_case()), get_value);
202+
}
203+
204+
const auto size = numeric_cast<std::size_t>(get_value(a.length()));
205+
const exprt content = get_value(a.content());
206+
const auto &array = expr_try_dynamic_cast<array_exprt>(content);
207+
if(!size || !array)
208+
return {};
209+
210+
const auto &ops = array->operands();
211+
INVARIANT(*size == ops.size(), "operands size should match string size");
212+
213+
std::vector<mp_integer> result;
214+
const mp_integer unknown('?');
215+
const auto &insert = std::back_inserter(result);
216+
std::transform(
217+
ops.begin(), ops.end(), insert, [unknown](const exprt &e) { // NOLINT
218+
if(const auto i = numeric_cast<mp_integer>(e))
219+
return *i;
220+
return unknown;
221+
});
222+
return result;
223+
}
224+
225+
array_string_exprt
226+
make_string(const std::vector<mp_integer> &array, const array_typet &array_type)
227+
{
228+
const typet &char_type = array_type.subtype();
229+
array_exprt array_expr(array_type);
230+
const auto &insert = std::back_inserter(array_expr.operands());
231+
std::transform(
232+
array.begin(), array.end(), insert, [&](const mp_integer &i) { // NOLINT
233+
return from_integer(i, char_type);
234+
});
235+
return to_array_string_expr(array_expr);
236+
}
237+
238+
std::vector<mp_integer> string_concatenation_builtin_functiont::eval(
239+
const std::vector<mp_integer> &input1_value,
240+
const std::vector<mp_integer> &input2_value,
241+
const std::vector<mp_integer> &args_value) const
242+
{
243+
const std::size_t start_index =
244+
args_value.size() > 0 && args_value[0] > 0 ? args_value[0].to_ulong() : 0;
245+
const std::size_t end_index = args_value.size() > 1 && args_value[1] > 0
246+
? args_value[1].to_ulong()
247+
: input2_value.size();
248+
249+
std::vector<mp_integer> result(input1_value);
250+
result.insert(
251+
result.end(),
252+
input2_value.begin() + start_index,
253+
input2_value.begin() + end_index);
254+
return result;
255+
}
256+
257+
std::vector<mp_integer> string_insertion_builtin_functiont::eval(
258+
const std::vector<mp_integer> &input1_value,
259+
const std::vector<mp_integer> &input2_value,
260+
const std::vector<mp_integer> &args_value) const
261+
{
262+
PRECONDITION(args_value.size() >= 1 || args_value.size() <= 3);
263+
const std::size_t &offset = numeric_cast_v<std::size_t>(args_value[0]);
264+
const std::size_t &start =
265+
args_value.size() > 1 ? numeric_cast_v<std::size_t>(args_value[1]) : 0;
266+
const std::size_t &end = args_value.size() > 2
267+
? numeric_cast_v<std::size_t>(args_value[2])
268+
: input2_value.size();
269+
270+
std::vector<mp_integer> result(input1_value);
271+
result.insert(
272+
result.begin() + offset,
273+
input2_value.begin() + start,
274+
input2_value.end() + end);
275+
return result;
276+
}
277+
278+
optionalt<exprt> string_insertion_builtin_functiont::eval(
279+
const std::function<exprt(const exprt &)> &get_value) const
280+
{
281+
const auto &input1_value = eval_string(input1, get_value);
282+
const auto &input2_value = eval_string(input2, get_value);
283+
if(!input2_value.has_value() || !input1_value.has_value())
284+
return {};
285+
286+
std::vector<mp_integer> arg_values;
287+
const auto &insert = std::back_inserter(arg_values);
288+
const mp_integer unknown('?');
289+
std::transform(
290+
args.begin(), args.end(), insert, [&](const exprt &e) { // NOLINT
291+
if(const auto val = numeric_cast<mp_integer>(get_value(e)))
292+
return *val;
293+
return unknown;
294+
});
295+
296+
const auto result_value = eval(*input1_value, *input2_value, arg_values);
297+
const auto length = from_integer(result_value.size(), result.length().type());
298+
const array_typet type(result.type().subtype(), length);
299+
return make_string(result_value, type);
300+
}
301+
178302
/// Construct a string_builtin_functiont object from a function application
179303
/// \return a unique pointer to the created object, this unique pointer is empty
180304
/// if the function does not correspond to one of the supported
@@ -192,8 +316,8 @@ static std::unique_ptr<string_builtin_functiont> to_string_builtin_function(
192316
new string_insertion_builtin_functiont(fun_app.arguments(), array_pool));
193317

194318
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));
319+
return util_make_unique<string_concatenation_builtin_functiont>(
320+
fun_app.arguments(), array_pool);
197321

198322
return {};
199323
}

src/solvers/refinement/string_refinement_util.h

+29
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,9 @@ class string_builtin_functiont
157157
{
158158
return {};
159159
}
160+
161+
virtual optionalt<exprt>
162+
eval(const std::function<exprt(const exprt &)> &get_value) const = 0;
160163
};
161164

162165
/// String builtin_function transforming one string into another
@@ -200,6 +203,32 @@ class string_insertion_builtin_functiont : public string_builtin_functiont
200203
{
201204
return {input1, input2};
202205
}
206+
207+
/// Evaluate the result from a concrete valuation of the arguments
208+
virtual std::vector<mp_integer> eval(
209+
const std::vector<mp_integer> &input1_value,
210+
const std::vector<mp_integer> &input2_value,
211+
const std::vector<mp_integer> &args_value) const;
212+
213+
optionalt<exprt>
214+
eval(const std::function<exprt(const exprt &)> &get_value) const override;
215+
};
216+
217+
class string_concatenation_builtin_functiont final
218+
: public string_insertion_builtin_functiont
219+
{
220+
public:
221+
string_concatenation_builtin_functiont(
222+
const std::vector<exprt> &fun_args,
223+
array_poolt &array_pool)
224+
: string_insertion_builtin_functiont(fun_args, array_pool)
225+
{
226+
}
227+
228+
std::vector<mp_integer> eval(
229+
const std::vector<mp_integer> &input1_value,
230+
const std::vector<mp_integer> &input2_value,
231+
const std::vector<mp_integer> &args_value) const override;
203232
};
204233

205234
/// String creation from other types

0 commit comments

Comments
 (0)