@@ -223,6 +223,76 @@ static exprt n_Xes(mp_integer n, exprt op)
223223 return n_Xes (n - 1 , X_exprt{std::move (op)});
224224}
225225
226+ // Returns a set of match conditions (given as LTL formula)
227+ struct ltl_sequence_matcht
228+ {
229+ ltl_sequence_matcht (exprt __cond, mp_integer __length)
230+ : cond(std::move(__cond)), length(std::move(__length))
231+ {
232+ }
233+
234+ exprt cond; // LTL
235+ mp_integer length; // match_end - match_start
236+ };
237+
238+ std::vector<ltl_sequence_matcht> LTL_sequence_matches (const exprt &sequence)
239+ {
240+ if (!is_SVA_sequence_operator (sequence))
241+ {
242+ // atomic proposition
243+ return {{sequence, 0 }};
244+ }
245+ else if (sequence.id () == ID_sva_sequence_concatenation)
246+ {
247+ auto &concatenation = to_sva_sequence_concatenation_expr (sequence);
248+ auto matches_lhs = LTL_sequence_matches (concatenation.lhs ());
249+ auto matches_rhs = LTL_sequence_matches (concatenation.rhs ());
250+
251+ if (matches_lhs.empty () || matches_rhs.empty ())
252+ return {};
253+
254+ std::vector<ltl_sequence_matcht> result;
255+ // cross product
256+ for (auto &match_lhs : matches_lhs)
257+ for (auto &match_rhs : matches_rhs)
258+ {
259+ auto rhs_delayed = n_Xes (match_lhs.length , match_rhs.cond );
260+ result.emplace_back (
261+ and_exprt{match_lhs.cond , rhs_delayed},
262+ match_lhs.length + match_rhs.length );
263+ }
264+ return result;
265+ }
266+ else if (sequence.id () == ID_sva_cycle_delay)
267+ {
268+ auto &delay = to_sva_cycle_delay_expr (sequence);
269+ auto matches = LTL_sequence_matches (delay.op ());
270+ auto from_int = numeric_cast_v<mp_integer>(delay.from ());
271+
272+ if (matches.empty ())
273+ return {};
274+
275+ if (delay.to ().is_nil ())
276+ {
277+ for (auto &match : matches)
278+ {
279+ // delay as instructed
280+ match.length += from_int;
281+ match.cond = n_Xes (from_int, match.cond );
282+ }
283+ return matches;
284+ }
285+ else
286+ return {};
287+ }
288+ else
289+ {
290+ return {}; // unsupported
291+ }
292+ }
293+
294+ // / takes an SVA property as input, and returns an equivalent LTL property,
295+ // / or otherwise {}.
226296std::optional<exprt> SVA_to_LTL (exprt expr)
227297{
228298 // Some SVA is directly mappable to LTL
@@ -316,25 +386,82 @@ std::optional<exprt> SVA_to_LTL(exprt expr)
316386 else
317387 return {};
318388 }
319- else if (expr.id () == ID_sva_overlapped_implication)
389+ else if (
390+ expr.id () == ID_sva_overlapped_implication ||
391+ expr.id () == ID_sva_non_overlapped_implication)
320392 {
321- auto &implication = to_sva_overlapped_implication_expr (expr);
322- auto rec_lhs = SVA_to_LTL (implication.lhs ());
323- auto rec_rhs = SVA_to_LTL (implication.rhs ());
324- if (rec_lhs.has_value () && rec_rhs.has_value ())
325- return implies_exprt{rec_lhs.value (), rec_rhs.value ()};
326- else
393+ auto &implication = to_sva_implication_base_expr (expr);
394+ auto matches = LTL_sequence_matches (implication.sequence ());
395+
396+ if (matches.empty ())
397+ return {};
398+
399+ // All matches must be followed
400+ // by the property being true
401+ exprt::operandst conjuncts;
402+
403+ auto property_rec = SVA_to_LTL (implication.property ());
404+
405+ if (!property_rec.has_value ())
327406 return {};
407+
408+ for (auto &match : matches)
409+ {
410+ auto delay =
411+ match.length + (expr.id () == ID_sva_non_overlapped_implication ? 1 : 0 );
412+ auto delayed_property = n_Xes (delay, property_rec.value ());
413+ conjuncts.push_back (implies_exprt{match.cond , delayed_property});
414+ }
415+
416+ return conjunction (conjuncts);
328417 }
329- else if (expr.id () == ID_sva_non_overlapped_implication)
418+ else if (
419+ expr.id () == ID_sva_nonoverlapped_followed_by ||
420+ expr.id () == ID_sva_overlapped_followed_by)
330421 {
331- auto &implication = to_sva_non_overlapped_implication_expr (expr);
332- auto rec_lhs = SVA_to_LTL (implication.lhs ());
333- auto rec_rhs = SVA_to_LTL (implication.rhs ());
334- if (rec_lhs.has_value () && rec_rhs.has_value ())
335- return implies_exprt{rec_lhs.value (), X_exprt{rec_rhs.value ()}};
336- else
422+ auto &followed_by = to_sva_followed_by_expr (expr);
423+ auto matches = LTL_sequence_matches (followed_by.sequence ());
424+
425+ if (matches.empty ())
337426 return {};
427+
428+ // There must be at least one match that is followed
429+ // by the property being true
430+ exprt::operandst disjuncts;
431+
432+ auto property_rec = SVA_to_LTL (followed_by.property ());
433+
434+ if (!property_rec.has_value ())
435+ return {};
436+
437+ for (auto &match : matches)
438+ {
439+ auto delay =
440+ match.length + (expr.id () == ID_sva_nonoverlapped_followed_by ? 1 : 0 );
441+ auto delayed_property = n_Xes (delay, property_rec.value ());
442+ disjuncts.push_back (and_exprt{match.cond , delayed_property});
443+ }
444+
445+ return disjunction (disjuncts);
446+ }
447+ else if (expr.id () == ID_sva_sequence_property)
448+ {
449+ auto &sequence = to_sva_sequence_property_expr (expr).sequence ();
450+
451+ // evaluates to true if there's at least one match of the sequence
452+ auto matches = LTL_sequence_matches (sequence);
453+
454+ if (matches.empty ())
455+ return {};
456+
457+ exprt::operandst disjuncts;
458+
459+ for (auto &match : matches)
460+ {
461+ disjuncts.push_back (match.cond );
462+ }
463+
464+ return disjunction (disjuncts);
338465 }
339466 else if (!has_temporal_operator (expr))
340467 {
0 commit comments