123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474(**************************************************************************)(* *)(* This file is part of Aorai plug-in of Frama-C. *)(* *)(* Copyright (C) 2007-2023 *)(* CEA (Commissariat à l'énergie atomique et aux énergies *)(* alternatives) *)(* INRIA (Institut National de Recherche en Informatique et en *)(* Automatique) *)(* INSA (Institut National des Sciences Appliquees) *)(* *)(* you can redistribute it and/or modify it under the terms of the GNU *)(* Lesser General Public License as published by the Free Software *)(* Foundation, version 2.1. *)(* *)(* It is distributed in the hope that it will be useful, *)(* but WITHOUT ANY WARRANTY; without even the implied warranty of *)(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *)(* GNU Lesser General Public License for more details. *)(* *)(* See the GNU Lesser General Public License version 2.1 *)(* for more details (enclosed in the file licenses/LGPLv2.1). *)(* *)(**************************************************************************)openCil_typesopenAutomaton_astletpretty_clausefmtl=Format.fprintffmt"@[<2>[%a@]]@\n"(Pretty_utils.pp_list~sep:",@ "Pretty_automaton.Typed.print_condition)lletpretty_dnffmtl=Format.fprintffmt"@[<2>[%a@]]@\n"(Pretty_utils.pp_listpretty_clause)lletopposite_rel=function|Rlt->Rge|Rgt->Rle|Rge->Rlt|Rle->Rgt|Req->Rneq|Rneq->ReqletreccondToDNFcond=(*Typing : condition --> list of list of terms (disjunction of conjunction of terms)
DNF(term) = {{term}}
DNF(a or b) = DNF(a) \/ DNF(b)
DNF(a and b) = Composition (DNF(a),DNF(b))
DNF(not a) = tmp = DNF(a)
composition (tmp)
negation of each term
*)matchcondwith|TOr(c1,c2)->(condToDNFc1)@(condToDNFc2)|TAnd(c1,c2)->letd1,d2=(condToDNFc1),(condToDNFc2)inList.rev(List.fold_left(funlclauseclauses1->(List.map(funclauses2->clauses1@clauses2)d2)@lclause)[]d1)|TNot(c)->beginmatchcwith|TOr(c1,c2)->condToDNF(TAnd(TNot(c1),TNot(c2)))|TAnd(c1,c2)->condToDNF(TOr(TNot(c1),TNot(c2)))|TNot(c1)->condToDNFc1|TTrue->condToDNFTFalse|TFalse->condToDNFTTrue|TRel(rel,t1,t2)->[[TRel(opposite_relrel,t1,t2)]]|_ast->[[TNot(t)]]end|TTrue->[[TTrue]]|TFalse->[]|_ast->[[t]]letremoveTermtermlterm=List.fold_left(funtreatedt->matchterm,twith|TCall(kf1,None),TCall(kf2,_)|TReturnkf1,TReturnkf2whenKernel_function.equalkf1kf2->treated|TCall(kf1,Someb1),TCall(kf2,Someb2)whenKernel_function.equalkf1kf2&&Datatype.String.equalb1.b_nameb2.b_name->treated|_->t::treated)[]lterm(** Given a list of terms (representing a conjunction),
if a positive call or return is present,
then all negative ones are obvious and removed *)letpositiveCallOrRetclause=try(* Step 1: find a positive information TCall or TReturn. *)letpositive,computePositive=List.fold_left(fun(positive,treatedasres)term->matchtermwith|TCall(kf1,None)->beginmatchpositivewith|None->(Someterm,term::treated)|Some(TCall(kf2,None))->ifKernel_function.equalkf1kf2thenreselseraiseExit|Some(TReturn_)->raiseExit|Some(TCall(kf2,Some_)asterm2)->ifKernel_function.equalkf1kf2thenSometerm,term::removeTermterm2treatedelseraiseExit|_->Aorai_option.fatal"inconsistent environment in positiveCallOrRet"end|TCall(kf1,Someb1)->beginmatchpositivewith|None->(Someterm,term::treated)|Some(TCall(kf2,None))->ifKernel_function.equalkf1kf2thenreselseraiseExit|Some(TReturn_)->raiseExit|Some(TCall(kf2,Someb2))->ifKernel_function.equalkf1kf2thenifDatatype.String.equalb1.b_nameb2.b_namethenreselsepositive,term::treatedelseraiseExit|_->Aorai_option.fatal"inconsistent environment in positiveCallOrRet"end|TReturnkf1->beginmatchpositivewith|None->(Someterm,term::treated)|Some(TReturnkf2)->ifKernel_function.equalkf1kf2thenreselseraiseExit|Some(TCall_)->raiseExit|_->Aorai_option.fatal"inconsistent environment in positiveCallOrRet"end|_->positive,term::treated)(None,[])clauseinletcomputePositive=List.revcomputePositivein(* Step 2 : Remove negatives not enough expressive *)matchpositivewith|None->computePositive|Some(TCall(kf1,None))->List.rev(List.fold_left(funtreatedterm->matchtermwith|TNot(TCall(kf2,_))->ifKernel_function.equalkf1kf2thenraiseExit(* Positive information more specific than negative *)elsetreated|TNot(TReturn_)->treated|_->term::treated)[]computePositive)|Some(TCall(kf1,Someb1))->List.rev(List.fold_left(funtreatedterm->matchtermwith|TNot(TCall(kf2,None))->ifKernel_function.equalkf1kf2thenraiseExit(* Positive information more specific than negative *)elsetreated|TNot(TCall(kf2,Someb2))->ifKernel_function.equalkf1kf2thenifDatatype.String.equalb1.b_nameb2.b_namethenraiseExitelseterm::treatedelsetreated|TNot(TReturn_)->treated|_->term::treated)[]computePositive)|Some(TReturnkf1)->List.rev(List.fold_left(funtreatedterm->matchtermwith|TNot(TCall_)->treated|TNot(TReturnkf2)->(* Two opposite information *)ifKernel_function.equalkf1kf2thenraiseExitelsetreated|_->term::treated)[]computePositive)|_->Aorai_option.fatal"inconsistent environment in positiveCallOrRet"withExit->[TFalse](* contradictory requirements for current event. *)letrel_are_equals(rel1,t11,t12)(rel2,t21,t22)=rel1=rel2&&Logic_utils.is_same_termt11t21&&Logic_utils.is_same_termt12t22letswap_rel(rel,t1,t2)=letrel=matchrelwith|Rlt->Rgt|Rle->Rge|Rge->Rle|Rgt->Rlt|Req->Req|Rneq->Rneqin(rel,t2,t1)letcontradict_relr1(rel2,t21,t22)=rel_are_equalsr1(opposite_relrel2,t21,t22)||rel_are_equals(swap_relr1)(opposite_relrel2,t21,t22)letrectermsAreEqualterm1term2=matchterm1,term2with|TTrue,TTrue|TFalse,TFalse->true|TCall(a,None),TCall(b,None)|TReturna,TReturnb->Kernel_function.equalab|TCall(f1,Someb1),TCall(f2,Someb2)->Kernel_function.equalf1f2&&Datatype.String.equalb1.b_nameb2.b_name|TNot(TRel(rel1,t11,t12)),TRel(rel2,t21,t22)|TRel(rel1,t11,t12),TNot(TRel(rel2,t21,t22))->contradict_rel(rel1,t11,t12)(rel2,t21,t22)|TNot(a),TNot(b)->termsAreEqualab|TRel(rel1,t11,t12),TRel(rel2,t21,t22)->rel_are_equals(rel1,t11,t12)(rel2,t21,t22)|_->falseletnegative_termterm=matchtermwith|TNot(c)->c|TCall_|TReturn_|TRel_->TNotterm|TTrue->TFalse|TFalse->TTrue|TAnd(_,_)|TOr(_,_)->Aorai_option.fatal"not a term of DNF clause"(** Simplify redundant relations. *)letsimplifyclause=tryList.rev(List.fold_left(funclauseterm->matchtermwith|TTrue|TNot(TFalse)->clause|TFalse|TNot(TTrue)->raiseExit|_->ifList.exists(termsAreEqual(negative_termterm))clausethenraiseExit;ifList.exists(termsAreEqualterm)clausethenclauseelseterm::clause)[]clause)withExit->[TFalse](** true iff clause1 <: clause2*)letclausesAreSubSetEqclause1clause2=(List.for_all(funt1->List.exists(funt2->termsAreEqualt1t2)clause2)clause1)(** true iff clause1 <: clause2 and clause2 <: clause1 *)letclausesAreEqualclause1clause2=clausesAreSubSetEqclause1clause2&&clausesAreSubSetEqclause2clause1(** return the clauses list named lclauses without any clause c such as cl <: c *)letremoveClauselclausescl=List.filter(func->not(clausesAreSubSetEqclc))lclauses(* Obvious version. *)letnegativeClauseclause=List.mapnegative_termclauseletsimplifyClausesclauses=tryList.rev(List.fold_left(funaccc->(* If 2 clauses are C and not C then their disjunction implies true *)ifList.exists(clausesAreEqual(negativeClausec))accthenraiseExit(* If an observed clause c2 is included inside the current clause
then the current is not added *)elseif(List.exists(func2->clausesAreSubSetEqc2c)acc)thenacc(* If the current clause is included inside an observed clause
c2 then the current is added and c2 is removed *)elseif(List.exists(func2->clausesAreSubSetEqcc2)acc)thenc::(removeClauseaccc)(* If no simplification then c is add to the list *)elsec::acc)[]clauses)withExit->[[]]lettort1t2=matcht1,t2withTTrue,_|_,TTrue->TTrue|TFalse,t|t,TFalse->t|_,_->TOr(t1,t2)lettandt1t2=matcht1,t2withTTrue,t|t,TTrue->t|TFalse,_|_,TFalse->TFalse|_,_->TAnd(t1,t2)letrectnott=matchtwith|TTrue->TFalse|TFalse->TTrue|TNott->t|TAnd(TCall(c,b),t)->TOr(TNot(TCall(c,b)),TAnd(TCall(c,b),tnott))|TAnd(TReturnc,t)->TOr(TNot(TReturnc),TAnd(TReturnc,tnott))|TAnd(t1,t2)->TOr(tnott1,tnott2)|TOr(t1,t2)->TAnd(tnott1,tnott2)|TRel(rel,t1,t2)->TRel(opposite_relrel,t1,t2)|TCall_|TReturn_->TNottlettandsl=List.fold_righttandlTTruelettorsl=List.fold_righttorlTFalse(** Given a DNF condition, it returns a condition in Automaton_ast.condition form.
WARNING : empty lists not supported
*)letdnfToCondd=tors(List.maptandsd)letsimplClauseclausednf=matchclausewith|[]|[TTrue]|[TNotTFalse]->[[]]|[TFalse]|[TNotTTrue]->dnf|_->clause::dnf(** Given a condition, this function does some logical simplifications.
It returns both the simplified condition and a disjunction of
conjunctions of parametrized call or return.
*)letsimplifyCondcondition=Aorai_option.debug"initial condition: %a"Pretty_automaton.Typed.print_conditioncondition;(* Step 1 : Condition is translate into Disjunctive Normal Form *)letres1=condToDNFconditioninAorai_option.debug"initial dnf: %a"pretty_dnfres1;(* Step 2 : Positive Call/Ret are used to simplify negative ones *)letres=List.rev(List.fold_left(funlclausesclause->simplClause(positiveCallOrRetclause)lclauses)[]res1)inAorai_option.debug"after step 2: %a"pretty_dnfres;(* Step 3 : simplification between exprs inside a clause *)letres=List.rev(List.fold_left(funlclausesclause->simplClause(simplifyclause)lclauses)[]res)inAorai_option.debug"after step 3: %a"pretty_dnfres;(* Step 4 : simplification between clauses *)letres=simplifyClausesresinAorai_option.debug"after step 4: %a"pretty_dnfres;((dnfToCondres),res)(** Given a list of transitions, this function returns the same list of
transition with simplifyCond done on its cross condition *)letsimplifyTranstransl=List.fold_left(fun(ltr,lpcond)tr->let(crossCond,pcond)=simplifyCond(tr.cross)in(* pcond stands for parametrized condition :
disjunction of conjunctions of parametrized call/return *)lettr'={trwithcross=crossCond}inAorai_option.debug"condition is %a, dnf is %a"Pretty_automaton.Typed.print_conditioncrossCondpretty_dnfpcond;iftr'.cross<>TFalsethen(tr'::ltr,pcond::lpcond)else(ltr,lpcond))([],[])(List.revtransl)(** Given a DNF condition, it returns the same condition simplified according
to the context (function name and status). Hence, the returned condition
is without any Call/Return stmts.
*)letsimplifyDNFwrtCtxdnfkf1status=Aorai_option.debug"Before simplification: %a"pretty_dnfdnf;letrecsimplConditionc=matchcwith|TCall(kf2,None)->ifKernel_function.equalkf1kf2&&status=Automaton_ast.CallthenTTrueelseTFalse|TCall(kf2,Some_)->ifKernel_function.equalkf1kf2&&status=Automaton_ast.CallthencelseTFalse|TReturnkf2->ifKernel_function.equalkf1kf2&&status=Automaton_ast.ReturnthenTTrueelseTFalse|TNotc->tnot(simplConditionc)|TAnd(c1,c2)->tand(simplConditionc1)(simplConditionc2)|TOr(c1,c2)->tor(simplConditionc1)(simplConditionc2)|TTrue|TFalse|TRel_->cinletsimplCNFwrtCtxcnf=tands(List.mapsimplConditioncnf)inletres=tors(List.mapsimplCNFwrtCtxdnf)inAorai_option.debug"After simplification: %a"Pretty_automaton.Typed.print_conditionres;res(*
Tests :
Working :
==========
simplifyCond(PAnd(POr(PTrue,PIndexedExp("a")),PNot(PAnd(PFalse,PIndexedExp("b")))));;
- : condition = PTrue
simplifyCond(POr(PAnd(PNot(PIndexedExp("b")),POr(PTrue,PIndexedExp("a"))),PAnd(PIndexedExp("a"),PNot(PFalse))));;
- : condition = POr (PIndexedExp "a", PNot (PIndexedExp "b"))
simplifyCond(PAnd(PAnd(PCall("a"),PIndexedExp "a"),PAnd(PNot(PCall("a")),PNot(PIndexedExp "a"))));;
- : condition = PFalse
simplifyCond(PAnd(PIndexedExp "a",PNot(PIndexedExp "a")));;
- : condition = PFalse
simplifyCond(PAnd(PCall("a"),PCall("a")));;
- : condition = PCall "a"
simplifyCond(PAnd(PIndexedExp("a"),PNot(PIndexedExp("a"))));;
- : condition = PFalse
simplifyCond(POr(PCall("a"),PNot(PCall("a"))));;
- : condition = PTrue
simplifyCond(PAnd(POr(PCall("a"),PCall("b")),POr(PNot(PCall("a")),PCall("b")))) ;;
- : condition = PCall "b"
simplifyCond(POr (PCall "b", PCall "b"));;
- : condition = PCall "b"
Simplifications to be done :
=========================
*)(*
Local Variables:
compile-command: "make -C ../../.."
End:
*)