Skip to content

Commit 58b12de

Browse files
committed
String solver: avoid cubic formulas for string-to-int conversions
Previously when the string radix was unknown we generated a conjunction of no-overflow checks which was (in total) cubic in the number of digits in the string being converted to an integer. For instance, it would define a series of 'sum's, like: sum0 = digit0 sum1 = digit0 * radix + digit1 sum2 = (digit0 * radix + digit1) * radix + digit2 Where the definitions are truly inlined like this and don't use symbols to represent sum0...n, so |sumn| is O(n), and the sum of all sums is O(n^2) Then for each digit it would emit an overflow check: constraint2 = size = 2 => nooverflow(sum1) constraint3 = size = 3 => nooverflow(sum1) && nooverflow(sum2) constraint4 = size = 4 => nooverflow(sum1) && nooverflow(sum2) && nooverflow(sum3) However those sums are again expanded inline, meaning the sum of all constraints is O(n^3) The obvious fix is to define symbols for sum0... sumn and then constraint0... constraintn, but this slows the solver down considerably for small formulas (e.g. Byte.parseByte, with max size 3 digits), so I compromise here by replacing the constraints with: constraint2 = size >= 2 => nooverflow(sum1) constraint3 = size >= 3 => nooverflow(sum2) constraint4 = size >= 4 => nooverflow(sum3) Thus the formula size is quadratic not cubic but still fast for cases where the radix is known.
1 parent cccdfd9 commit 58b12de

File tree

1 file changed

+16
-10
lines changed

1 file changed

+16
-10
lines changed

src/solvers/strings/string_constraint_generator_valueof.cpp

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -385,7 +385,6 @@ string_constraint_generatort::add_axioms_for_characters_in_integer_string(
385385

386386
const equal_exprt starts_with_minus(str[0], from_integer('-', char_type));
387387
const constant_exprt zero_expr = from_integer(0, type);
388-
exprt::operandst digit_constraints;
389388

390389
exprt sum = get_numeric_value_from_character(
391390
str[0], char_type, type, strict_formatting, radix_ul);
@@ -424,31 +423,38 @@ string_constraint_generatort::add_axioms_for_characters_in_integer_string(
424423
// a digit, which is `max_string_length - 2` because of the space left for
425424
// a minus sign. That assumes that we were able to identify the radix. If we
426425
// weren't then we check for overflow on every index.
426+
optionalt<exprt> no_overflow;
427427
if(size - 1 >= max_string_length - 2 || radix_ul == 0)
428428
{
429-
const and_exprt no_overflow(
429+
no_overflow = and_exprt{
430430
equal_exprt(sum, div_exprt(radix_sum, radix)),
431-
binary_relation_exprt(new_sum, ID_ge, radix_sum));
432-
digit_constraints.push_back(no_overflow);
431+
binary_relation_exprt(new_sum, ID_ge, radix_sum)};
432+
/*symbol_exprt result =
433+
fresh_symbol("string_to_int_no_overflow_check", no_overflow.type());
434+
constraints.existential.push_back(equal_exprt(result, no_overflow));
435+
digit_constraints.push_back(result);*/
433436
}
434437
sum = new_sum;
435438

436-
const equal_exprt premise =
437-
equal_to(array_pool.get_or_create_length(str), size);
439+
exprt length_expr = array_pool.get_or_create_length(str);
438440

439-
if(!digit_constraints.empty())
441+
if(no_overflow.has_value())
440442
{
441-
const implies_exprt a5(premise, conjunction(digit_constraints));
443+
const binary_predicate_exprt string_length_gte_size{
444+
length_expr, ID_ge, from_integer(size, length_expr.type())};
445+
const implies_exprt a5(string_length_gte_size, *no_overflow);
442446
constraints.existential.push_back(a5);
443447
}
444448

449+
const equal_exprt string_length_equals_size = equal_to(length_expr, size);
450+
445451
const implies_exprt a6(
446-
and_exprt(premise, not_exprt(starts_with_minus)),
452+
and_exprt(string_length_equals_size, not_exprt(starts_with_minus)),
447453
equal_exprt(input_int, sum));
448454
constraints.existential.push_back(a6);
449455

450456
const implies_exprt a7(
451-
and_exprt(premise, starts_with_minus),
457+
and_exprt(string_length_equals_size, starts_with_minus),
452458
equal_exprt(input_int, unary_minus_exprt(sum)));
453459
constraints.existential.push_back(a7);
454460
}

0 commit comments

Comments
 (0)