@@ -224,6 +224,82 @@ 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
+ auto rhs_delayed = n_Xes (match_lhs.length , match_rhs.cond );
267
+ result.emplace_back (
268
+ and_exprt{match_lhs.cond , rhs_delayed},
269
+ match_lhs.length + match_rhs.length );
270
+ }
271
+ return result;
272
+ }
273
+ else if (sequence.id () == ID_sva_cycle_delay)
274
+ {
275
+ auto &delay = to_sva_cycle_delay_expr (sequence);
276
+ auto matches = LTL_sequence_matches (delay.op ());
277
+ auto from_int = numeric_cast_v<mp_integer>(delay.from ());
278
+
279
+ if (matches.empty ())
280
+ return {};
281
+
282
+ if (delay.to ().is_nil ())
283
+ {
284
+ for (auto &match : matches)
285
+ {
286
+ // delay as instructed
287
+ match.length += from_int;
288
+ match.cond = n_Xes (from_int, match.cond );
289
+ }
290
+ return matches;
291
+ }
292
+ else
293
+ return {};
294
+ }
295
+ else
296
+ {
297
+ return {}; // unsupported
298
+ }
299
+ }
300
+
301
+ // / takes an SVA property as input, and returns an equivalent LTL property,
302
+ // / or otherwise {}.
227
303
std::optional<exprt> SVA_to_LTL (exprt expr)
228
304
{
229
305
// Some SVA is directly mappable to LTL
@@ -317,25 +393,104 @@ std::optional<exprt> SVA_to_LTL(exprt expr)
317
393
else
318
394
return {};
319
395
}
320
- else if (expr.id () == ID_sva_overlapped_implication)
396
+ else if (
397
+ expr.id () == ID_sva_overlapped_implication ||
398
+ expr.id () == ID_sva_non_overlapped_implication)
321
399
{
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
400
+ auto &implication = to_sva_implication_base_expr (expr);
401
+ auto matches = LTL_sequence_matches (implication.sequence ());
402
+
403
+ if (matches.empty ())
328
404
return {};
405
+
406
+ // All matches must be followed
407
+ // by the property being true
408
+ exprt::operandst conjuncts;
409
+
410
+ auto property_rec = SVA_to_LTL (implication.property ());
411
+
412
+ if (!property_rec.has_value ())
413
+ return {};
414
+
415
+ for (auto &match : matches)
416
+ {
417
+ if (match.empty () && expr.id () == ID_sva_overlapped_followed_by)
418
+ {
419
+ // ignore the empty match
420
+ }
421
+ else
422
+ {
423
+ auto delay =
424
+ match.length + (expr.id () == ID_sva_non_overlapped_implication ? 1 : 0 ) - 1 ;
425
+ auto delayed_property = n_Xes (delay, property_rec.value ());
426
+ conjuncts.push_back (implies_exprt{match.cond , delayed_property});
427
+ }
428
+ }
429
+
430
+ return conjunction (conjuncts);
329
431
}
330
- else if (expr.id () == ID_sva_non_overlapped_implication)
432
+ else if (
433
+ expr.id () == ID_sva_nonoverlapped_followed_by ||
434
+ expr.id () == ID_sva_overlapped_followed_by)
331
435
{
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
436
+ auto &followed_by = to_sva_followed_by_expr (expr);
437
+ auto matches = LTL_sequence_matches (followed_by.sequence ());
438
+
439
+ if (matches.empty ())
440
+ return {};
441
+
442
+ // There must be at least one match that is followed
443
+ // by the property being true
444
+ exprt::operandst disjuncts;
445
+
446
+ auto property_rec = SVA_to_LTL (followed_by.property ());
447
+
448
+ if (!property_rec.has_value ())
338
449
return {};
450
+
451
+ for (auto &match : matches)
452
+ {
453
+ if (match.empty () && expr.id () == ID_sva_overlapped_followed_by)
454
+ {
455
+ // ignore the empty match
456
+ }
457
+ else
458
+ {
459
+ auto delay =
460
+ match.length + (expr.id () == ID_sva_nonoverlapped_followed_by ? 1 : 0 ) - 1 ;
461
+ auto delayed_property = n_Xes (delay, property_rec.value ());
462
+ disjuncts.push_back (and_exprt{match.cond , delayed_property});
463
+ }
464
+ }
465
+
466
+ return disjunction (disjuncts);
467
+ }
468
+ else if (expr.id () == ID_sva_sequence_property)
469
+ {
470
+ // should have been turned into sva_implicit_weak or sva_implicit_strong
471
+ PRECONDITION (false );
472
+ }
473
+ else if (
474
+ expr.id () == ID_sva_weak || expr.id () == ID_sva_strong ||
475
+ expr.id () == ID_sva_implicit_weak || expr.id () == ID_sva_implicit_strong)
476
+ {
477
+ auto &sequence = to_sva_sequence_property_expr_base (expr).sequence ();
478
+
479
+ // evaluates to true if there's at least one non-empty match of the sequence
480
+ auto matches = LTL_sequence_matches (sequence);
481
+
482
+ if (matches.empty ())
483
+ return {};
484
+
485
+ exprt::operandst disjuncts;
486
+
487
+ for (auto &match : matches)
488
+ {
489
+ if (!match.empty ())
490
+ disjuncts.push_back (match.cond );
491
+ }
492
+
493
+ return disjunction (disjuncts);
339
494
}
340
495
else if (!has_temporal_operator (expr))
341
496
{
0 commit comments