@@ -224,6 +224,85 @@ static exprt n_Xes(mp_integer n, exprt op)
224
224
return n_Xes (n - 1 , X_exprt{std::move (op)});
225
225
}
226
226
227
+ // Returns a set of match conditions (given as LTL formula)
228
+ struct ltl_sequence_matcht
229
+ {
230
+ ltl_sequence_matcht (exprt __cond, mp_integer __length)
231
+ : cond(std::move(__cond)), length(std::move(__length))
232
+ {
233
+ PRECONDITION (length >= 0 );
234
+ }
235
+
236
+ exprt cond; // LTL
237
+ mp_integer length; // match_end - match_start + 1
238
+
239
+ bool empty () const
240
+ {
241
+ return length == 0 ;
242
+ }
243
+ };
244
+
245
+ std::vector<ltl_sequence_matcht> LTL_sequence_matches (const exprt &sequence)
246
+ {
247
+ if (!is_SVA_sequence_operator (sequence))
248
+ {
249
+ // atomic proposition
250
+ return {{sequence, 1 }};
251
+ }
252
+ else if (sequence.id () == ID_sva_sequence_concatenation)
253
+ {
254
+ auto &concatenation = to_sva_sequence_concatenation_expr (sequence);
255
+ auto matches_lhs = LTL_sequence_matches (concatenation.lhs ());
256
+ auto matches_rhs = LTL_sequence_matches (concatenation.rhs ());
257
+
258
+ if (matches_lhs.empty () || matches_rhs.empty ())
259
+ return {};
260
+
261
+ std::vector<ltl_sequence_matcht> result;
262
+ // cross product
263
+ for (auto &match_lhs : matches_lhs)
264
+ for (auto &match_rhs : matches_rhs)
265
+ {
266
+ // Concatenation is overlapping, hence deduct one from
267
+ // the length.
268
+ auto delay = match_lhs.length - 1 ;
269
+ auto rhs_delayed = n_Xes (delay, match_rhs.cond );
270
+ result.emplace_back (
271
+ and_exprt{match_lhs.cond , rhs_delayed},
272
+ match_lhs.length + match_rhs.length - 1 );
273
+ }
274
+ return result;
275
+ }
276
+ else if (sequence.id () == ID_sva_cycle_delay)
277
+ {
278
+ auto &delay = to_sva_cycle_delay_expr (sequence);
279
+ auto matches = LTL_sequence_matches (delay.op ());
280
+ auto from_int = numeric_cast_v<mp_integer>(delay.from ());
281
+
282
+ if (matches.empty ())
283
+ return {};
284
+
285
+ if (delay.to ().is_nil ())
286
+ {
287
+ for (auto &match : matches)
288
+ {
289
+ // delay as instructed
290
+ match.length += from_int;
291
+ match.cond = n_Xes (from_int, match.cond );
292
+ }
293
+ return matches;
294
+ }
295
+ else
296
+ return {};
297
+ }
298
+ else
299
+ {
300
+ return {}; // unsupported
301
+ }
302
+ }
303
+
304
+ // / takes an SVA property as input, and returns an equivalent LTL property,
305
+ // / or otherwise {}.
227
306
std::optional<exprt> SVA_to_LTL (exprt expr)
228
307
{
229
308
// Some SVA is directly mappable to LTL
@@ -317,25 +396,104 @@ std::optional<exprt> SVA_to_LTL(exprt expr)
317
396
else
318
397
return {};
319
398
}
320
- else if (expr.id () == ID_sva_overlapped_implication)
399
+ else if (
400
+ expr.id () == ID_sva_overlapped_implication ||
401
+ expr.id () == ID_sva_non_overlapped_implication)
321
402
{
322
- auto &implication = to_sva_overlapped_implication_expr (expr);
323
- auto rec_lhs = SVA_to_LTL (implication.lhs ());
324
- auto rec_rhs = SVA_to_LTL (implication.rhs ());
325
- if (rec_lhs.has_value () && rec_rhs.has_value ())
326
- return implies_exprt{rec_lhs.value (), rec_rhs.value ()};
327
- else
403
+ auto &implication = to_sva_implication_base_expr (expr);
404
+ auto matches = LTL_sequence_matches (implication.sequence ());
405
+
406
+ if (matches.empty ())
328
407
return {};
408
+
409
+ // All matches must be followed
410
+ // by the property being true
411
+ exprt::operandst conjuncts;
412
+
413
+ auto property_rec = SVA_to_LTL (implication.property ());
414
+
415
+ if (!property_rec.has_value ())
416
+ return {};
417
+
418
+ for (auto &match : matches)
419
+ {
420
+ const auto overlapped = expr.id () == ID_sva_overlapped_implication;
421
+ if (match.empty () && overlapped)
422
+ {
423
+ // ignore the empty match
424
+ }
425
+ else
426
+ {
427
+ auto delay = match.length + (overlapped ? 0 : 1 ) - 1 ;
428
+ auto delayed_property = n_Xes (delay, property_rec.value ());
429
+ conjuncts.push_back (implies_exprt{match.cond , delayed_property});
430
+ }
431
+ }
432
+
433
+ return conjunction (conjuncts);
329
434
}
330
- else if (expr.id () == ID_sva_non_overlapped_implication)
435
+ else if (
436
+ expr.id () == ID_sva_nonoverlapped_followed_by ||
437
+ expr.id () == ID_sva_overlapped_followed_by)
331
438
{
332
- auto &implication = to_sva_non_overlapped_implication_expr (expr);
333
- auto rec_lhs = SVA_to_LTL (implication.lhs ());
334
- auto rec_rhs = SVA_to_LTL (implication.rhs ());
335
- if (rec_lhs.has_value () && rec_rhs.has_value ())
336
- return implies_exprt{rec_lhs.value (), X_exprt{rec_rhs.value ()}};
337
- else
439
+ auto &followed_by = to_sva_followed_by_expr (expr);
440
+ auto matches = LTL_sequence_matches (followed_by.sequence ());
441
+
442
+ if (matches.empty ())
443
+ return {};
444
+
445
+ // There must be at least one match that is followed
446
+ // by the property being true
447
+ exprt::operandst disjuncts;
448
+
449
+ auto property_rec = SVA_to_LTL (followed_by.property ());
450
+
451
+ if (!property_rec.has_value ())
338
452
return {};
453
+
454
+ for (auto &match : matches)
455
+ {
456
+ const auto overlapped = expr.id () == ID_sva_overlapped_followed_by;
457
+ if (match.empty () && overlapped)
458
+ {
459
+ // ignore the empty match
460
+ }
461
+ else
462
+ {
463
+ auto delay = match.length + (overlapped ? 0 : 1 ) - 1 ;
464
+ auto delayed_property = n_Xes (delay, property_rec.value ());
465
+ disjuncts.push_back (and_exprt{match.cond , delayed_property});
466
+ }
467
+ }
468
+
469
+ return disjunction (disjuncts);
470
+ }
471
+ else if (expr.id () == ID_sva_sequence_property)
472
+ {
473
+ // should have been turned into sva_implicit_weak or sva_implicit_strong
474
+ PRECONDITION (false );
475
+ }
476
+ else if (
477
+ expr.id () == ID_sva_weak || expr.id () == ID_sva_strong ||
478
+ expr.id () == ID_sva_implicit_weak || expr.id () == ID_sva_implicit_strong)
479
+ {
480
+ auto &sequence = to_sva_sequence_property_expr_base (expr).sequence ();
481
+
482
+ // evaluates to true if there's at least one non-empty match of the sequence
483
+ auto matches = LTL_sequence_matches (sequence);
484
+
485
+ if (matches.empty ())
486
+ return {};
487
+
488
+ exprt::operandst disjuncts;
489
+
490
+ for (auto &match : matches)
491
+ {
492
+ if (!match.empty ())
493
+ disjuncts.push_back (match.cond );
494
+ }
495
+
496
+ return disjunction (disjuncts);
339
497
}
340
498
else if (!has_temporal_operator (expr))
341
499
{
0 commit comments