Skip to content

Commit 540c691

Browse files
authored
Merge pull request #284 from diffblue/BDD-GFp
ebmc: implement GFp check with BDDs
2 parents 429476e + 609a2ab commit 540c691

File tree

3 files changed

+200
-67
lines changed

3 files changed

+200
-67
lines changed

regression/ebmc/BDD/BDD_SVA1.desc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ BDD_SVA1.sv
88
^\[top\.property\.p2\] always \(top\.counter == 3 \|-> \(nexttime top\.counter == 4\)\): UNKNOWN$
99
^\[top\.property\.p3\] always \(top\.counter == 3 \|=> top\.counter == 4\): PROVED$
1010
^\[top\.property\.p4\] always \(top\.counter == 3 \|=> \(nexttime top\.counter == 5\)\): UNKNOWN$
11-
^\[top\.property\.p5\] always eventually top\.counter == 8: UNKNOWN$
11+
^\[top\.property\.p5\] always eventually top\.counter == 8: PROVED$
1212
^\[top\.property\.p6\] always \(top\.counter == 0 \|-> \(eventually top\.counter == 8\)\): UNKNOWN$
1313
^\[top\.property\.p7\] always \(top\.counter == 0 \|-> \(top\.counter <= 5 until top\.counter == 6\)\): UNKNOWN$
1414
^\[top\.property\.p8\] always \(top\.counter == 0 \|-> \(top\.counter <= 5 until_with top\.counter == 5\)\): UNKNOWN$

regression/ebmc/BDD/BDD_SVA1.sv

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
module top(input clock);
22

33
reg my_bit;
4-
reg [31:0] counter;
4+
reg [7:0] counter;
55

66
initial my_bit=1;
77
initial counter=0;

src/ebmc/bdd_engine.cpp

Lines changed: 198 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,10 @@ class bdd_enginet
121121
void compute_counterexample(
122122
propertyt &,
123123
unsigned number_of_timeframes);
124+
125+
void AGp(propertyt &);
126+
void AGAFp(propertyt &);
127+
void just_p(propertyt &);
124128
};
125129

126130
/*******************************************************************\
@@ -425,100 +429,229 @@ void bdd_enginet::check_property(propertyt &property)
425429
!has_temporal_operator(to_unary_expr(expr).op());
426430
};
427431

432+
// special treatment for AG AFp
433+
auto is_AG_AFp = [](const exprt &expr) {
434+
return (expr.id() == ID_AG || expr.id() == ID_sva_always) &&
435+
(to_unary_expr(expr).op().id() == ID_AF ||
436+
to_unary_expr(expr).op().id() == ID_sva_eventually) &&
437+
!has_temporal_operator(to_unary_expr(to_unary_expr(expr).op()).op());
438+
};
439+
428440
if(is_AGp(property.expr))
429441
{
430-
const exprt &sub_expr = to_unary_expr(property.expr).op();
431-
BDD p=property2BDD(sub_expr);
442+
AGp(property);
443+
}
444+
else if(is_AG_AFp(property.expr))
445+
{
446+
AGAFp(property);
447+
}
448+
else if(!has_temporal_operator(property.expr))
449+
{
450+
just_p(property);
451+
}
452+
}
432453

433-
// Start with !p, and go backwards until saturation or we hit an
434-
// initial state.
435-
436-
BDD states=!p;
437-
unsigned iteration=0;
438-
439-
for(const auto &c : constraints_BDDs)
440-
states = states & c;
454+
/*******************************************************************\
441455
442-
std::size_t peak_bdd_nodes=0;
456+
Function: bdd_enginet::AGp
443457
444-
while(true)
445-
{
446-
iteration++;
447-
message.statistics() << "Iteration " << iteration << messaget::eom;
458+
Inputs:
448459
449-
// do we have an initial state?
450-
BDD intersection=states;
451-
452-
for(const auto &i : initial_BDDs)
453-
intersection=intersection & i;
460+
Outputs:
454461
455-
peak_bdd_nodes=std::max(peak_bdd_nodes, mgr.number_of_nodes());
462+
Purpose:
456463
457-
if(!intersection.is_false())
458-
{
459-
property.refuted();
460-
message.status() << "Property refuted" << messaget::eom;
461-
compute_counterexample(property, iteration);
462-
break;
463-
}
464-
465-
// make the states be expressed in terms of 'next' variables
466-
BDD states_next=current_to_next(states);
464+
\*******************************************************************/
467465

468-
// now conjoin with transition relation
469-
BDD conjunction=states_next;
470-
471-
for(const auto &t : transition_BDDs)
472-
conjunction = conjunction & t;
473-
474-
for(const auto &c : constraints_BDDs)
475-
conjunction = conjunction & c;
466+
void bdd_enginet::AGp(propertyt &property)
467+
{
468+
const exprt &sub_expr = to_unary_expr(property.expr).op();
469+
BDD p = property2BDD(sub_expr);
476470

477-
// now project away 'next' variables
478-
BDD pre_image=project_next(conjunction);
479-
480-
// compute union
481-
BDD set_union=states | pre_image;
471+
// Start with !p, and go backwards until saturation or we hit an
472+
// initial state.
482473

483-
// have we saturated?
484-
if((set_union==states).is_true())
485-
{
486-
property.proved();
487-
message.status() << "Property proved" << messaget::eom;
488-
break;
489-
}
474+
BDD states = !p;
475+
unsigned iteration = 0;
490476

491-
states=set_union;
477+
for(const auto &c : constraints_BDDs)
478+
states = states & c;
492479

493-
peak_bdd_nodes=std::max(peak_bdd_nodes, mgr.number_of_nodes());
494-
}
495-
}
496-
else if(!has_temporal_operator(property.expr))
480+
std::size_t peak_bdd_nodes = 0;
481+
482+
while(true)
497483
{
498-
// We check whether the BDD for the negation of the property
499-
// contains an initial state.
500-
exprt negation=negate_property(property.expr);
501-
BDD states=property2BDD(negation);
484+
iteration++;
485+
message.statistics() << "Iteration " << iteration << messaget::eom;
502486

503487
// do we have an initial state?
504-
BDD intersection=states;
505-
488+
BDD intersection = states;
489+
506490
for(const auto &i : initial_BDDs)
507491
intersection = intersection & i;
508492

509-
for(const auto &c : constraints_BDDs)
510-
intersection = intersection & c;
493+
peak_bdd_nodes = std::max(peak_bdd_nodes, mgr.number_of_nodes());
511494

512495
if(!intersection.is_false())
513496
{
514497
property.refuted();
515498
message.status() << "Property refuted" << messaget::eom;
499+
compute_counterexample(property, iteration);
500+
break;
516501
}
517-
else
502+
503+
// make the states be expressed in terms of 'next' variables
504+
BDD states_next = current_to_next(states);
505+
506+
// now conjoin with transition relation
507+
BDD conjunction = states_next;
508+
509+
for(const auto &t : transition_BDDs)
510+
conjunction = conjunction & t;
511+
512+
for(const auto &c : constraints_BDDs)
513+
conjunction = conjunction & c;
514+
515+
// now project away 'next' variables
516+
BDD pre_image = project_next(conjunction);
517+
518+
// compute union
519+
BDD set_union = states | pre_image;
520+
521+
// have we saturated?
522+
if((set_union == states).is_true())
518523
{
519524
property.proved();
520525
message.status() << "Property proved" << messaget::eom;
526+
break;
527+
}
528+
529+
states = set_union;
530+
531+
peak_bdd_nodes = std::max(peak_bdd_nodes, mgr.number_of_nodes());
532+
}
533+
}
534+
535+
/*******************************************************************\
536+
537+
Function: bdd_enginet::AGAFp
538+
539+
Inputs:
540+
541+
Outputs:
542+
543+
Purpose:
544+
545+
\*******************************************************************/
546+
547+
void bdd_enginet::AGAFp(propertyt &property)
548+
{
549+
const exprt &sub_expr = to_unary_expr(to_unary_expr(property.expr).op()).op();
550+
BDD p = property2BDD(sub_expr);
551+
552+
// Start with p, and go backwards until saturation.
553+
// AG AFp holds iff this includes all initial states.
554+
555+
BDD good_states = p;
556+
unsigned iteration = 0;
557+
558+
for(const auto &c : constraints_BDDs)
559+
good_states = good_states & c;
560+
561+
std::size_t peak_bdd_nodes = 0;
562+
563+
while(true)
564+
{
565+
iteration++;
566+
message.statistics() << "Iteration " << iteration << messaget::eom;
567+
568+
// make the states be expressed in terms of 'next' variables
569+
BDD good_states_next = current_to_next(good_states);
570+
571+
// now conjoin with transition relation
572+
BDD conjunction = good_states_next;
573+
574+
for(const auto &t : transition_BDDs)
575+
conjunction = conjunction & t;
576+
577+
for(const auto &c : constraints_BDDs)
578+
conjunction = conjunction & c;
579+
580+
// now project away 'next' variables
581+
BDD pre_image = project_next(conjunction);
582+
583+
// compute union
584+
BDD set_union = good_states | pre_image;
585+
586+
// have we saturated?
587+
if((set_union == good_states).is_true())
588+
{
589+
message.progress() << "Fixedpoint reached" << messaget::eom;
590+
break;
521591
}
592+
593+
good_states = set_union;
594+
595+
peak_bdd_nodes = std::max(peak_bdd_nodes, mgr.number_of_nodes());
596+
}
597+
598+
// Does 'good_states' include all initial states?
599+
BDD union_with_initial = good_states;
600+
601+
for(const auto &i : initial_BDDs)
602+
union_with_initial = union_with_initial | i;
603+
604+
if((union_with_initial == good_states).is_true())
605+
{
606+
// proved
607+
property.proved();
608+
message.status() << "Property proved" << messaget::eom;
609+
}
610+
else
611+
{
612+
// refuted
613+
property.refuted();
614+
message.status() << "Property refuted" << messaget::eom;
615+
}
616+
}
617+
618+
/*******************************************************************\
619+
620+
Function: bdd_enginet::just_p
621+
622+
Inputs:
623+
624+
Outputs:
625+
626+
Purpose:
627+
628+
\*******************************************************************/
629+
630+
void bdd_enginet::just_p(propertyt &property)
631+
{
632+
// We check whether the BDD for the negation of the property
633+
// contains an initial state.
634+
exprt negation = negate_property(property.expr);
635+
BDD states = property2BDD(negation);
636+
637+
// do we have an initial state?
638+
BDD intersection = states;
639+
640+
for(const auto &i : initial_BDDs)
641+
intersection = intersection & i;
642+
643+
for(const auto &c : constraints_BDDs)
644+
intersection = intersection & c;
645+
646+
if(!intersection.is_false())
647+
{
648+
property.refuted();
649+
message.status() << "Property refuted" << messaget::eom;
650+
}
651+
else
652+
{
653+
property.proved();
654+
message.status() << "Property proved" << messaget::eom;
522655
}
523656
}
524657

0 commit comments

Comments
 (0)