@@ -225,6 +225,108 @@ static exprt n_Xes(mp_integer n, exprt op)
225225 return n_Xes (n - 1 , X_exprt{std::move (op)});
226226}
227227
228+ // Returns a set of match conditions (given as LTL formula)
229+ struct ltl_sequence_matcht
230+ {
231+ ltl_sequence_matcht (exprt __cond, mp_integer __length)
232+ : cond(std::move(__cond)), length(std::move(__length))
233+ {
234+ PRECONDITION (length >= 0 );
235+ }
236+
237+ exprt cond; // LTL
238+ mp_integer length; // match_end - match_start + 1
239+
240+ bool empty () const
241+ {
242+ return length == 0 ;
243+ }
244+ };
245+
246+ std::vector<ltl_sequence_matcht> LTL_sequence_matches (const exprt &sequence)
247+ {
248+ if (!is_SVA_sequence_operator (sequence))
249+ {
250+ // atomic proposition
251+ return {{sequence, 1 }};
252+ }
253+ else if (sequence.id () == ID_sva_sequence_concatenation)
254+ {
255+ auto &concatenation = to_sva_sequence_concatenation_expr (sequence);
256+ auto matches_lhs = LTL_sequence_matches (concatenation.lhs ());
257+ auto matches_rhs = LTL_sequence_matches (concatenation.rhs ());
258+
259+ if (matches_lhs.empty () || matches_rhs.empty ())
260+ return {};
261+
262+ std::vector<ltl_sequence_matcht> result;
263+ // cross product
264+ for (auto &match_lhs : matches_lhs)
265+ for (auto &match_rhs : matches_rhs)
266+ {
267+ // Concatenation is overlapping, hence deduct one from
268+ // the length.
269+ auto delay = match_lhs.length - 1 ;
270+ auto rhs_delayed = n_Xes (delay, match_rhs.cond );
271+ result.emplace_back (
272+ and_exprt{match_lhs.cond , rhs_delayed},
273+ match_lhs.length + match_rhs.length - 1 );
274+ }
275+ return result;
276+ }
277+ else if (sequence.id () == ID_sva_cycle_delay)
278+ {
279+ auto &delay = to_sva_cycle_delay_expr (sequence);
280+ auto matches = LTL_sequence_matches (delay.op ());
281+ auto from_int = numeric_cast_v<mp_integer>(delay.from ());
282+
283+ if (matches.empty ())
284+ return {};
285+
286+ if (delay.to ().is_nil ())
287+ {
288+ for (auto &match : matches)
289+ {
290+ // delay as instructed
291+ match.length += from_int;
292+ match.cond = n_Xes (from_int, match.cond );
293+ }
294+ return matches;
295+ }
296+ else if (delay.to ().id () == ID_infinity)
297+ {
298+ return {}; // can't encode
299+ }
300+ else if (delay.to ().is_constant ())
301+ {
302+ auto to_int = numeric_cast_v<mp_integer>(to_constant_expr (delay.to ()));
303+ std::vector<ltl_sequence_matcht> new_matches;
304+
305+ for (mp_integer i = from_int; i <= to_int; ++i)
306+ {
307+ for (const auto &match : matches)
308+ {
309+ // delay as instructed
310+ auto new_match = match;
311+ new_match.length += from_int;
312+ new_match.cond = n_Xes (i, match.cond );
313+ new_matches.push_back (std::move (new_match));
314+ }
315+ }
316+
317+ return new_matches;
318+ }
319+ else
320+ return {};
321+ }
322+ else
323+ {
324+ return {}; // unsupported
325+ }
326+ }
327+
328+ // / takes an SVA property as input, and returns an equivalent LTL property,
329+ // / or otherwise {}.
228330std::optional<exprt> SVA_to_LTL (exprt expr)
229331{
230332 // Some SVA is directly mappable to LTL
@@ -318,25 +420,104 @@ std::optional<exprt> SVA_to_LTL(exprt expr)
318420 else
319421 return {};
320422 }
321- else if (expr.id () == ID_sva_overlapped_implication)
423+ else if (
424+ expr.id () == ID_sva_overlapped_implication ||
425+ expr.id () == ID_sva_non_overlapped_implication)
322426 {
323- auto &implication = to_sva_overlapped_implication_expr (expr);
324- auto rec_lhs = SVA_to_LTL (implication.lhs ());
325- auto rec_rhs = SVA_to_LTL (implication.rhs ());
326- if (rec_lhs.has_value () && rec_rhs.has_value ())
327- return implies_exprt{rec_lhs.value (), rec_rhs.value ()};
328- else
427+ auto &implication = to_sva_implication_base_expr (expr);
428+ auto matches = LTL_sequence_matches (implication.sequence ());
429+
430+ if (matches.empty ())
431+ return {};
432+
433+ // All matches must be followed
434+ // by the property being true
435+ exprt::operandst conjuncts;
436+
437+ auto property_rec = SVA_to_LTL (implication.property ());
438+
439+ if (!property_rec.has_value ())
329440 return {};
441+
442+ for (auto &match : matches)
443+ {
444+ const auto overlapped = expr.id () == ID_sva_overlapped_implication;
445+ if (match.empty () && overlapped)
446+ {
447+ // ignore the empty match
448+ }
449+ else
450+ {
451+ auto delay = match.length + (overlapped ? 0 : 1 ) - 1 ;
452+ auto delayed_property = n_Xes (delay, property_rec.value ());
453+ conjuncts.push_back (implies_exprt{match.cond , delayed_property});
454+ }
455+ }
456+
457+ return conjunction (conjuncts);
330458 }
331- else if (expr.id () == ID_sva_non_overlapped_implication)
459+ else if (
460+ expr.id () == ID_sva_nonoverlapped_followed_by ||
461+ expr.id () == ID_sva_overlapped_followed_by)
332462 {
333- auto &implication = to_sva_non_overlapped_implication_expr (expr);
334- auto rec_lhs = SVA_to_LTL (implication.lhs ());
335- auto rec_rhs = SVA_to_LTL (implication.rhs ());
336- if (rec_lhs.has_value () && rec_rhs.has_value ())
337- return implies_exprt{rec_lhs.value (), X_exprt{rec_rhs.value ()}};
338- else
463+ auto &followed_by = to_sva_followed_by_expr (expr);
464+ auto matches = LTL_sequence_matches (followed_by.sequence ());
465+
466+ if (matches.empty ())
467+ return {};
468+
469+ // There must be at least one match that is followed
470+ // by the property being true
471+ exprt::operandst disjuncts;
472+
473+ auto property_rec = SVA_to_LTL (followed_by.property ());
474+
475+ if (!property_rec.has_value ())
339476 return {};
477+
478+ for (auto &match : matches)
479+ {
480+ const auto overlapped = expr.id () == ID_sva_overlapped_followed_by;
481+ if (match.empty () && overlapped)
482+ {
483+ // ignore the empty match
484+ }
485+ else
486+ {
487+ auto delay = match.length + (overlapped ? 0 : 1 ) - 1 ;
488+ auto delayed_property = n_Xes (delay, property_rec.value ());
489+ disjuncts.push_back (and_exprt{match.cond , delayed_property});
490+ }
491+ }
492+
493+ return disjunction (disjuncts);
494+ }
495+ else if (expr.id () == ID_sva_sequence_property)
496+ {
497+ // should have been turned into sva_implicit_weak or sva_implicit_strong
498+ PRECONDITION (false );
499+ }
500+ else if (
501+ expr.id () == ID_sva_weak || expr.id () == ID_sva_strong ||
502+ expr.id () == ID_sva_implicit_weak || expr.id () == ID_sva_implicit_strong)
503+ {
504+ auto &sequence = to_sva_sequence_property_expr_base (expr).sequence ();
505+
506+ // evaluates to true if there's at least one non-empty match of the sequence
507+ auto matches = LTL_sequence_matches (sequence);
508+
509+ if (matches.empty ())
510+ return {};
511+
512+ exprt::operandst disjuncts;
513+
514+ for (auto &match : matches)
515+ {
516+ if (!match.empty ())
517+ disjuncts.push_back (match.cond );
518+ }
519+
520+ return disjunction (disjuncts);
340521 }
341522 else if (expr.id () == ID_sva_s_until)
342523 {
0 commit comments