@@ -225,6 +225,108 @@ static exprt n_Xes(mp_integer n, exprt op)
225
225
return n_Xes (n - 1 , X_exprt{std::move (op)});
226
226
}
227
227
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 {}.
228
330
std::optional<exprt> SVA_to_LTL (exprt expr)
229
331
{
230
332
// Some SVA is directly mappable to LTL
@@ -318,25 +420,104 @@ std::optional<exprt> SVA_to_LTL(exprt expr)
318
420
else
319
421
return {};
320
422
}
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)
322
426
{
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 ())
329
440
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);
330
458
}
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)
332
462
{
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 ())
339
476
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);
340
521
}
341
522
else if (expr.id () == ID_sva_s_until)
342
523
{
0 commit comments