Skip to content

Commit 5481f2e

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 5481f2e

File tree

1 file changed

+13
-13
lines changed

1 file changed

+13
-13
lines changed

src/solvers/strings/string_constraint_generator_valueof.cpp

Lines changed: 13 additions & 13 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);
@@ -407,8 +406,7 @@ string_constraint_generatort::add_axioms_for_characters_in_integer_string(
407406
// (the first part avoid overflows in the multiplication and the second
408407
// one in the addition of the definition of sum_j)
409408
// For all 1<=size<=max_string_length we add axioms:
410-
// a5 : |res| == size =>
411-
// forall max_string_length-2 <= j < size. no_overflow_j
409+
// a5 : |res| >= size => no_overflow_j (only added when overflow possible)
412410
// a6 : |res| == size && res[0] is a digit for radix =>
413411
// input_int == sum_{size-1}
414412
// a7 : |res| == size && res[0] == '-' => input_int == -sum_{size-1}
@@ -424,31 +422,33 @@ string_constraint_generatort::add_axioms_for_characters_in_integer_string(
424422
// a digit, which is `max_string_length - 2` because of the space left for
425423
// a minus sign. That assumes that we were able to identify the radix. If we
426424
// weren't then we check for overflow on every index.
425+
optionalt<exprt> no_overflow;
427426
if(size - 1 >= max_string_length - 2 || radix_ul == 0)
428427
{
429-
const and_exprt no_overflow(
430-
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);
428+
no_overflow = and_exprt{equal_exprt(sum, div_exprt(radix_sum, radix)),
429+
binary_relation_exprt(new_sum, ID_ge, radix_sum)};
433430
}
434431
sum = new_sum;
435432

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

439-
if(!digit_constraints.empty())
435+
if(no_overflow.has_value())
440436
{
441-
const implies_exprt a5(premise, conjunction(digit_constraints));
437+
const binary_predicate_exprt string_length_ge_size{
438+
length_expr, ID_ge, from_integer(size, length_expr.type())};
439+
const implies_exprt a5(string_length_ge_size, *no_overflow);
442440
constraints.existential.push_back(a5);
443441
}
444442

443+
const equal_exprt string_length_equals_size = equal_to(length_expr, size);
444+
445445
const implies_exprt a6(
446-
and_exprt(premise, not_exprt(starts_with_minus)),
446+
and_exprt(string_length_equals_size, not_exprt(starts_with_minus)),
447447
equal_exprt(input_int, sum));
448448
constraints.existential.push_back(a6);
449449

450450
const implies_exprt a7(
451-
and_exprt(premise, starts_with_minus),
451+
and_exprt(string_length_equals_size, starts_with_minus),
452452
equal_exprt(input_int, unary_minus_exprt(sum)));
453453
constraints.existential.push_back(a7);
454454
}

0 commit comments

Comments
 (0)