123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436(**************************************************************************)(* *)(* This file is part of WP plug-in of Frama-C. *)(* *)(* Copyright (C) 2007-2024 *)(* CEA (Commissariat a l'energie atomique et aux energies *)(* alternatives) *)(* *)(* 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). *)(* *)(**************************************************************************)openLang.F(* -------------------------------------------------------------------------- *)(* --- Symbols registration --- *)(* -------------------------------------------------------------------------- *)letlibrary="memaddr"(* Warning: DO NOT register map types using this constructor: it hides types
needed by ProverWhy3 for typing terms of the form x[i].
*)lett_addr=Qed.Logic.Data(Lang.datatype~library"addr",[])lett_table=Qed.Logic.Data(Lang.datatype~library"table",[])lett_malloc=Qed.Logic.Array(Qed.Logic.Int,Qed.Logic.Int)letf_base=Lang.extern_f~library~result:Qed.Logic.Int~link:(Qed.Engine.F_subst("base","%1.base"))"base"letf_offset=Lang.extern_f~library~result:Qed.Logic.Int~link:(Qed.Engine.F_subst("offset","%1.offset"))"offset"letf_shift=Lang.extern_f~library~result:t_addr"shift"letf_global=Lang.extern_f~library~result:t_addr~category:Qed.Logic.Injection"global"letf_null=Lang.extern_f~library~result:t_addr"null"letp_addr_lt=Lang.extern_p~library~bool:"addr_lt_bool"~prop:"addr_lt"()letp_addr_le=Lang.extern_p~library~bool:"addr_le_bool"~prop:"addr_le"()letf_addr_of_int=Lang.extern_f~library~result:t_addr"addr_of_int"letf_int_of_addr=Lang.extern_f~library~result:Qed.Logic.Int"int_of_addr"letp_statically_allocated=Lang.extern_fp~library"statically_allocated"letf_table_of_base=Lang.extern_f~library~category:Qed.Logic.Function~result:t_table"table_of_base"letf_table_to_offset=Lang.extern_f~library~category:Qed.Logic.Injection~result:Qed.Logic.Int"table_to_offset"letp_valid_rd=Lang.extern_fp~library"valid_rd"letp_valid_rw=Lang.extern_fp~library"valid_rw"letp_valid_obj=Lang.extern_fp~library"valid_obj"letp_invalid=Lang.extern_fp~library"invalid"letp_separated=Lang.extern_fp~library"separated"letp_included=Lang.extern_fp~library"included"(* base -> region *)letf_region=Lang.extern_f~coloring:true~library~result:Qed.Logic.Int"region"(* allocation-table -> prop *)letp_linked=Lang.extern_fp~coloring:true~library"linked"(* -------------------------------------------------------------------------- *)(* --- API --- *)(* -------------------------------------------------------------------------- *)(* Basic constructors and getters *)letbaseaddr=e_funf_base[addr]letoffsetaddr=e_funf_offset[addr]letnull=constant(e_funf_null[])letglobalbase=e_funf_global[base]letshiftaddroffset=e_funf_shift[addr;offset]letmk_addrbaseoffset=shift(globalbase)offset(* Comparisons *)letaddr_ltaddr1addr2=p_callp_addr_lt[addr1;addr2]letaddr_leaddr1addr2=p_callp_addr_le[addr1;addr2](* Regions *)letregionbase=e_funf_region[base]letlinkedmemory=p_callp_linked[memory](* Validity *)letvalid_rdallocaddrsize=p_callp_valid_rd[alloc;addr;size]letvalid_rwallocaddrsize=p_callp_valid_rw[alloc;addr;size]letvalid_objallocaddr=p_callp_valid_obj[alloc;addr]letinvalidallocaddrsize=p_callp_invalid[alloc;addr;size](* Physical addresses *)letaddr_of_inti=e_funf_addr_of_int[i]letint_of_addraddr=e_funf_int_of_addr[addr]letstatic_allocaddr=p_callp_statically_allocated[addr](* This last function is not exposed, it does not have a particular meaning in
ACSL, and is used only for int/addr conversions.
*)letin_uintptr_rangeaddr=p_hyps[static_alloc@@baseaddr]@@Cint.range(Ctypes.c_ptr())@@int_of_addraddr(* Table of offsets *)letbase_offsetbaseoffset=letoffset_index=e_funf_table_of_base[base]ine_funf_table_to_offset[offset_index;offset](** Returns the offset in {i bytes} from the {i logic} offset
(which is a memory cell index, actually) *)(* -------------------------------------------------------------------------- *)(* --- Qed Simplifiers --- *)(* -------------------------------------------------------------------------- *)(*
Pointer arithmetic for structure access and array access could be
defined directly using the record [{ base = p.base; offset = p.offset
+ c*i + c' }]. However that gives very bad triggers for the memory
model axiomatization, so `shift p (c*i+c')` was used instead. It is
not sufficient for user axiomatisation because memory access in
axioms require trigger with arithmetic operators which is badly
handled by provers. So for each c and c', ie for each kind of
structure access and array access a specific function is used
`shift_xxx`.
Moreover no simplification of `shift_xxx` is done for keeping the
same terms in axioms and the goal. `base` and `offset` function
simplify all the `shift_xxx` because it seems they don't appear
often in axioms and they are useful for simplifying `separated`,
`assigns` and pointer comparisons in goals.
To sum up memory access should match, but not `\base`, `\offset`,
`\separated`, ...
*)typeaddr_builtin={base:termlist->term;offset:termlist->term;}moduleADDR_BUILTIN=WpContext.Static(structtypekey=Lang.lfuntypedata=addr_builtinletname="MemMemory.ADDR_BUILTIN"includeLang.Funend)letphi_basel=matchreprlwith|Fun(f,[p;_])whenf==f_shift->basep|Fun(f,[b])whenf==f_global->b|Fun(f,[])whenf==f_null->e_zero|Fun(f,args)->(ADDR_BUILTIN.findf).baseargs|_->raiseNot_foundletphi_offsetl=matchreprlwith|Fun(f,[p;k])whenf==f_shift->e_add(offsetp)k|Fun(f,_)whenf==f_global||f==f_null->e_zero|Fun(f,args)->(ADDR_BUILTIN.findf).offsetargs|_->raiseNot_foundletphi_shiftfpi=matchreprpwith|Fun(g,[q;j])whenf==g->e_funf[q;e_addij]|_->raiseNot_foundleteq_shiftab=letp=baseainletq=basebinleti=offsetainletj=offsetbinifi==jthenp_equalpqelsematchis_equalpqwith|No->p_false|Yes->p_equalij|Maybe->raiseNot_foundleteq_shift_genphiab=tryphiabwithNot_found->eq_shiftabletnop_=raiseNot_foundletregister?(base=nop)?(offset=nop)?equal?(linear=false)lfun=beginifbase!=nop||offset!=nopthenADDR_BUILTIN.definelfun{base;offset};iflinearthenset_builtin_2lfun(phi_shiftlfun);letphi_equal=matchequalwith|None->eq_shift|Somephi->eq_shift_genphiinset_builtin_eqplfunphi_equal;end(* -------------------------------------------------------------------------- *)(* --- Simplifier for 'separated' --- *)(* -------------------------------------------------------------------------- *)letr_separated=function|[p;a;q;b]->ifa==e_one&&b==e_onethene_neqpqelsebeginleta_negative=e_leqae_zeroinletb_negative=e_leqbe_zeroinifa_negative==e_true||b_negative==e_truethene_trueelseletbp=basepinletbq=baseqinmatchis_true(e_eqbpbq)with|No->e_true(* Have S *)|Yeswhen(a_negative==e_false&&b_negative==e_false)->(* Reduced to S *)letp_ofs=offsetpinletq_ofs=offsetqinletp_ofs'=e_addp_ofsainletq_ofs'=e_addq_ofsbine_or[e_leqq_ofs'p_ofs;e_leqp_ofs'q_ofs]|_->raiseNot_foundend|_->raiseNot_foundletis_separatedargs=is_true(r_separatedargs)(* -------------------------------------------------------------------------- *)(* --- Simplifier for 'included' --- *)(* -------------------------------------------------------------------------- *)(* See: tests/why3/test_memory.why
logic a : int
logic b : int
predicate R = p.base = q.base
/\ (q.offset <= p.offset)
/\ (p.offset + a <= q.offset + b)
predicate included = 0 < a -> ( 0 <= b and R )
predicate a_empty = a <= 0
predicate b_negative = b < 0
lemma SAME_P: p=q -> (R <-> a<=b)
lemma SAME_A: a=b -> (R <-> p=q)
goal INC_P: p=q -> (included <-> ( 0 < a -> a <= b )) (by SAME_P)
goal INC_A: a=b -> 0 < a -> (included <-> R) (by SAME_A)
goal INC_1: a_empty -> (included <-> true)
goal INC_2: b_negative -> (included <-> a_empty)
goal INC_3: not R -> (included <-> a_empty)
goal INC_4: not a_empty -> not b_negative -> (included <-> R)
*)letr_included=function|[p;a;q;b]->ife_eqpq==e_truethene_imply[e_lte_zeroa](e_leqab)(* INC_P *)elseif(e_eqab==e_true)&&(e_lte_zeroa==e_true)thene_eqpq(* INC_A *)elsebeginleta_empty=e_leqae_zeroinletb_negative=e_ltbe_zeroinifa_empty==e_truethene_true(* INC_1 *)elseifb_negative==e_truethena_empty(* INC_2 *)elseletbp=basepinletbq=baseqinmatchis_true(e_eqbpbq)with|No->a_empty(* INC_3 *)|Yeswhen(a_empty==e_false&&b_negative==e_false)->(* INC_4 *)letp_ofs=offsetpinletq_ofs=offsetqinifa==bthene_eqp_ofsq_ofselseletp_ofs'=e_addp_ofsainletq_ofs'=e_addq_ofsbine_and[e_leqq_ofsp_ofs;e_leqp_ofs'q_ofs']|_->raiseNot_foundend|_->raiseNot_foundletis_includedargs=is_true(r_includedargs)(* -------------------------------------------------------------------------- *)(* --- Simplifier for int/addr conversion --- *)(* -------------------------------------------------------------------------- *)letphi_int_of_addrp=ifp==nullthene_zeroelsematchreprpwith|Fun(f,[a])whenf==f_addr_of_int->a|_->raiseNot_foundletphi_addr_of_intp=ifp==e_zerothennullelseraiseNot_found(* -------------------------------------------------------------------------- *)(* --- Simplifier for (in)validity --- *)(* -------------------------------------------------------------------------- *)letnull_basep=e_eq(e_funf_base[p])e_zero(* See: tests/why3/test_memory.why *)(* - lemma valid_rd_null: forall m n p. p.base = 0 -> (n <= 0 <-> valid_rd m p n)
- lemma valid_rw_null: forall m n p. p.base = 0 -> (n <= 0 <-> valid_rw m p n)
*)letr_valid_unref=function|[_;p;n]whendecide(null_basep)->e_leqne_zero|_->raiseNot_found(* - lemma valid_obj_null: forall m n. valid_obj m null n *)letr_valid_obj=function|[_;p]whendecide(e_eqpnull)->e_true|_->raiseNot_found(* - lemma invalid_null: forall m n p. p.base = 0 -> invalid m p n *)letr_invalid=function|[_;p;_]whendecide(null_basep)->e_true|_->raiseNot_found(* -------------------------------------------------------------------------- *)(* --- Simplifiers Registration --- *)(* -------------------------------------------------------------------------- *)let()=Context.registerbeginfun()->set_builtin_1f_basephi_base;set_builtin_1f_offsetphi_offset;set_builtin_2f_shift(phi_shiftf_shift);set_builtin_eqpf_shifteq_shift;set_builtin_eqpf_globaleq_shift;set_builtinp_separatedr_separated;set_builtinp_includedr_included;set_builtin_1f_addr_of_intphi_addr_of_int;set_builtin_1f_int_of_addrphi_int_of_addr;set_builtinp_invalidr_invalid;set_builtinp_valid_rdr_valid_unref;set_builtinp_valid_rwr_valid_unref;set_builtinp_valid_objr_valid_obj;end(* -------------------------------------------------------------------------- *)(* --- Identify lfun --- *)(* -------------------------------------------------------------------------- *)letis_p_valid_rdlf=lf==p_valid_rdletis_p_valid_rwlf=lf==p_valid_rwletis_p_valid_objlf=lf==p_valid_objletis_p_invalidlf=lf==p_invalidletis_f_globallf=lf==f_global(* -------------------------------------------------------------------------- *)(* --- Range Comparison --- *)(* -------------------------------------------------------------------------- *)typerange=|LOCofterm*term(* loc - size *)|RANGEofterm*Vset.set(* base - range offset *)letrange~shift~addrof~sizeof=function|Sigs.Rloc(obj,loc)->LOC(addrofloc,sizeofobj)|Sigs.Rrange(loc,obj,Somea,Someb)->lets=sizeofobjinletp=addrof(shiftlocobja)inletn=e_muls(e_rangeab)inLOC(p,n)|Sigs.Rrange(loc,_obj,None,None)->RANGE(base(addrofloc),Vset.rangeNoneNone)|Sigs.Rrange(loc,obj,Somea,None)->lets=sizeofobjinRANGE(base(addrofloc),Vset.range(Some(e_mulsa))None)|Sigs.Rrange(loc,obj,None,Someb)->lets=sizeofobjinRANGE(base(addrofloc),Vset.rangeNone(Some(e_mulsb)))letrange_set=function|LOC(l,n)->leta=offsetlinletb=e_addaninbasel,Vset.range(Somea)(Someb)|RANGE(base,set)->base,setletr_includedr1r2=matchr1,r2with|LOC(l1,n1),LOC(l2,n2)->p_callp_included[l1;n1;l2;n2]|_->letbase1,set1=range_setr1inletbase2,set2=range_setr2inp_if(p_equalbase1base2)(Vset.subsetset1set2)(Vset.is_emptyset1)letr_disjointr1r2=matchr1,r2with|LOC(l1,n1),LOC(l2,n2)->p_callp_separated[l1;n1;l2;n2]|_->letbase1,set1=range_setr1inletbase2,set2=range_setr2inp_imply(p_equalbase1base2)(Vset.disjointset1set2)letincluded~shift~addrof~sizeofs1s2=letrange=range~shift~addrof~sizeofinr_included(ranges1)(ranges2)letseparated~shift~addrof~sizeofs1s2=letrange=range~shift~addrof~sizeofinr_disjoint(ranges1)(ranges2)