123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407(**************************************************************************)(* *)(* This file is part of Frama-C. *)(* *)(* Copyright (C) 2007-2023 *)(* CEA (Commissariat à l'énergie atomique et aux énergies *)(* 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). *)(* *)(**************************************************************************)openCil_typesmoduleFC_file=FileopenCil_datatypeexceptionCannot_expandexceptionCannot_change(* Build the term [p+i], assuming that [p] has pointer type *)letplus_pi~locpi=ifInteger.(equalzeroi)thenpelseCil.mkBinOp~locPlusPIp(Cil.kinteger64~loci)(** This visitor also performs a deep copy. *)classpropagateprojectfnames~cast_intro=object(self)inheritVisitor.frama_c_copyproject(* Variables which have already been declared earlier in the list of
globals. Varinfos of the old project. *)valmutableknown_globals=Varinfo.Set.empty(* Variables whose declaration must be put before the global we are visiting.
Reset before each global. Varinfos of the _new_ project. *)valmutablemust_add_decl=Varinfo.Set.emptymethod!vstmt_auxs=(* Do not propagate on 'return' statements: one invariant of the AST is
that they must be of the form 'return v;' where 'v' is a variable *)matchs.skindwith|Return_->Cil.JustCopy|_->Cil.DoChildrenmethod!vfuncfundec=ifCil_datatype.Fundec.Set.is_emptyfnames||Cil_datatype.Fundec.Set.memfundecfnamesthenbeginPropagationParameters.feedback~level:2"propagated constant in function %s"(fundec.svar.vname);Cil.DoChildrenendelseCil.JustCopymethodprivateadd_decl_non_source_varvi=PropagationParameters.debug~level:2"Need to declare %a earlier"Printer.pp_varinfovi;letvi'=Visitor.visitFramacVarDecl(self:>Visitor.frama_c_visitor)viinmust_add_decl<-Varinfo.Set.addvi'must_add_decl;known_globals<-Varinfo.Set.addviknown_globals;ifCil.isFunctionTypevi.vtypethenbeginletkf=Globals.Functions.getviinletnew_kf=Visitor_behavior.Memo.kernel_functionself#behaviorkfinQueue.add(fun()->Globals.Functions.registernew_kf)self#get_filling_actions;end(* introduce a new cast from [oldt] to [newt] or do not expand [e] *)methodprivateadd_cast~ignore_const_cast~oldt~newte=(* strip the superfluous 'const' attribute (see bts #1787) on
pointed values. *)letoldt,newt=ifignore_const_castthenmatchCil.unrollTypeoldt,Cil.unrollTypenewtwith|TPtr(typ,attrs),TPtr(typ',attrs')->letdrop_constty=Cil.typeRemoveAttributes["const"]tyinTPtr(drop_consttyp,attrs),TPtr(drop_consttyp',attrs')|_->oldt,newtelseoldt,newtinletexp=Cil.mkCastT~oldt~newteinifcast_introthenexpelsematchexp.enodewith|CastE_->ifexp==e(* older cast, no new cast added *)thenexpelsebegin(* without [cast_intro], introducing such a cast is not
allowed: do not expand [e] *)PropagationParameters.debug~level:2"Need a cast introduction (force using -scf-allow-cast option)";raiseCannot_expandend|_->(* remember the change done by [mkCastT] (if any).
note that [mkCastT] make some modifications, even if it
does not introduce a new cast. *)exp(* Make sure that [expr] is in the original project. *)methodprivatepropagatedexpr~ignore_const_cast=PropagationParameters.debug~level:2"Replacing %s%a?"(ifignore_const_castthen"(without const* cast) "else"")Printer.pp_expexpr;tryletloc=expr.elocinlettyp=Cil.typeOfexprinlettyp_e=Cil.unrollTypetypinbeginmatchtyp_ewith|(TInt_|TFloat_|TPtr_|TEnum_)->()|_->raiseCannot_expandend;letstmt=matchself#current_stmtwith|None->raiseCannot_change|Somes->sinletevaled=Eva.Results.(beforestmt|>eval_expexpr|>as_cvalue)inletb,m=Cvalue.V.find_lonely_bindingevaledinletcan_replacevi=(* can replace the current expr by [vi] iff (1) it is a source var, or
expansion of non-source var is allowed. *)(vi.vsource||PropagationParameters.ExpandLogicContext.get())&&(* (2) [vi] is bound in this function *)(vi.vglob||Option.fold~some:(Kernel_function.is_formal_or_localvi)~none:falseself#current_kf)inletchange_to=matchbwith|Base.Var(vi,_)|Base.Allocated(vi,_,_)whennot(Base.is_weakb)&&can_replacevi->ifvi.vglob&¬(Varinfo.Set.memviknown_globals)thenself#add_decl_non_source_varvi;PropagationParameters.debug"Trying replacing %a from a pointer value {&%a + %a}"Printer.pp_expexprBase.prettybIval.prettym;letoffset=Ival.project_intmin(* these are bytes *)letexpr'=tryifnot(Cil.isPointerTypetyp_e)thenraiseBit_utils.NoMatchingOffset;lettyp_pointed=Cil.unrollType(Cil.typeOf_pointedtyp_e)inifCil.isVoidTypetyp_pointedthenraiseBit_utils.NoMatchingOffset;letoffset=Integer.muloffsetInteger.eightinletm=Bit_utils.MatchTypetyp_pointedinletoff,_=Bit_utils.(find_offsetvi.vtype~offsetm)inCil.mkAddrOrStartOf~loc(Varvi,off)withBit_utils.NoMatchingOffset->(* Build [((char* )&t[idx])+rem] when vi is an array, or
[(char* )(&vi+idx)+rem] otherwise. Automatically simplify
when [idx] or [rem] is zero. *)letarray,idx,rem=letarray,sizeof_pointed=letarray=Cil.isArrayTypevi.vtypeinletsize=ifarraythenBit_utils.osizeof_pointedvi.vtypeelseBit_utils.osizeofvi.vtypeinarray,Int_Base.projectsizeinletdiv,rem=Integer.e_div_remoffsetsizeof_pointedinarray,div,reminletexpr'=ifarraythenletoff_idx=ifInteger.is_zeroidxthenNoOffsetelseIndex(Cil.kinteger64~locidx,NoOffset)inCil.mkAddrOrStartOf~loc(Varvi,off_idx)elseletstart=Cil.mkAddrOrStartOf~loc(Varvi,NoOffset)inplus_pi~locstartidxinifInteger.is_zeroremthenexpr'elseplus_pi~loc(self#add_cast~ignore_const_cast:false~oldt:(Cil.typeOfexpr')~newt:Cil.charPtrTypeexpr')remin(* preserve typing: propagating constant could change the type
of the expression. We have to put back the original type. *)self#add_cast~ignore_const_cast~oldt:(Cil.typeOfexpr')~newt:typexpr'|Base.Null->letconst_integermikind=tryletv=Ival.project_intminifnot(Cil.fitsInIntikindv)thenPropagationParameters.error"Constant found by Value (%a) \
does not fit inside type %a. Please report"Abstract_interp.Int.prettyvPrinter.pp_typtyp;Cil.kinteger64~loc~kind:ikindvwithIval.Not_Singleton_Int->raiseCannot_expandandconst_floatmfkind=tryletf=Ival.project_floatminletf=Fval.(F.to_float(project_floatf))inCil.kfloat~loc:expr.elocfkindfwithFval.Not_Singleton_Float->raiseCannot_expandin(matchtyp_ewith|TFloat(fkind,_)->const_floatmfkind|TInt(ikind,_)|TEnum({ekind=ikind},_)->const_integermikind|_->raiseCannot_expand)|Base.String_|Base.Var_|Base.Allocated_|Base.CLogic_Var_->raiseCannot_changeinPropagationParameters.debug"Replacing %a with %a"Printer.pp_expexprPrinter.pp_expchange_to;Somechange_towith|Cannot_change->None|Not_found|Cannot_expand|Cil.Not_representable|Abstract_interp.Error_Topase->PropagationParameters.debug"Replacement failed %s"(Printexc.to_stringe);Nonemethod!vexprexpr=(* nothing is done for [expr] already being a constant *)matchexpr.enodewith|Const(_)->Cil.DoChildren|_->begin(* Start by trying to constant-propagate all of [expr]. Casts are allowed
only if -scf-allow-cast is set *)matchself#propagatedexpr~ignore_const_cast:falsewith|Someexpr'->Cil.ChangeDoChildrenPost(expr',funx->x)|None->begin(* Global constant propagation of [expr] failed. We try a special
const-folding, AND simplify the sub-expressions in all cases *)matchexpr.enodewith|Lval(Memexp_mem,off)->begin(* [expr] is a Mem. Try to see if we can propagate [exp_mem] into
something simpler, because the result will be of the form
[Var _, offs'], which can be simplified under a [Mem]. This time,
we ignore const-related casts when simplifying [exp_mem], because
they will disappear when the l-value is dereferenced. *)matchself#propagatedexp_mem~ignore_const_cast:truewith|Someexp_mem'->letlv=Cil.new_exp~loc:expr.eloc(Lval(Cil.mkMem~addr:exp_mem'~off))inCil.ChangeDoChildrenPost(lv,funx->x)|None->Cil.DoChildrenend|_->Cil.DoChildrenendendmethod!vvdecv=ifv.vglobthenbeginknown_globals<-Varinfo.Set.addvknown_globals;end;Cil.DoChildrenmethod!vglob_auxg=must_add_decl<-Varinfo.Set.empty;(* Check if [g] has already been declared earlier, due to being used in
some earlier values. If so, we will skip [g]. We do this check now and
not in [add_decls], because [self#vvdec] will mark g as known. *)letg_is_known=matchgwith|GVarDecl(vi,_)|GFunDecl(_,vi,_)->Varinfo.Set.memviknown_globals|_->falseinletadd_declsl=(* Do not re-add a declaration for g if it is known. *)letl=ifg_is_knownthen[]elselin(* Add declarations for the globals that are referenced in g's propagated
value. *)Varinfo.Set.fold(funvil->PropagationParameters.feedback~level:2"Adding declaration of global %a"Printer.pp_varinfovi;letg'=ifCil.isFunctionTypevi.vtypethenGFunDecl(Cil.empty_funspec(),vi,vi.vdecl)elseGVarDecl(vi,vi.vdecl)ing'::l)must_add_decllinCil.DoChildrenPostadd_declsmethod!vlvallv=letsimplify(host,offaslv)=matchhostwith|Meme->Cil.mkMem~addr:e~off(* canonize in case the propagation
simplified [lv] *)|Var_->lvinCil.ChangeDoChildrenPost(lv,simplify)endmoduleResult_pair=Datatype.Pair_with_collections(Cil_datatype.Fundec.Set)(Datatype.Bool)(structletmodule_name="Constant_propagation.Register.Result_pair.t"end)moduleResult=State_builder.Hashtbl(Datatype.Hashtbl(Result_pair.Hashtbl)(Result_pair)(structletmodule_name="Semantical constant propagation"end))(Project.Datatype)(structletsize=7letname="Semantical constant propagation"letdependencies=[Eva.Analysis.self;PropagationParameters.CastIntro.self;PropagationParameters.Project_name.self]end)letselection_command_line_option=State_selection.singletonPropagationParameters.SemanticConstFolding.self(* add labels *)letgetfnames~cast_intro=Result.memo(fun_->Eva.Analysis.compute();letfresh_project=FC_file.create_project_from_visitor(PropagationParameters.Project_name.get())(funprj->newpropagateprjfnames~cast_intro)inletctx=Parameter_state.get_selection_context()inletctx=State_selection.diffctxselection_command_line_optioninProject.copy~selection:ctxfresh_project;fresh_project)(fnames,cast_intro)(** Constant Propagation *)letcompute()=PropagationParameters.feedback"beginning constant propagation";letfnames=PropagationParameters.SemanticConstFold.get()inletcast_intro=PropagationParameters.CastIntro.get()inletpropagated=getfnames~cast_introinifPropagationParameters.SemanticConstFolding.get()thenFC_file.pretty_ast~prj:propagated();letproject_name=Project.get_unique_namepropagatedinPropagationParameters.feedback"@[constant propagation done%t@]"(funfmt->ifproject_name<>PropagationParameters.Project_name.get()thenFormat.fprintffmt",@ result is in project@ `%s`"project_name)letcompute,self=letname="Constant_Propagation.compute"inletdeps=[PropagationParameters.SemanticConstFold.self;PropagationParameters.SemanticConstFolding.self;Result.self]inState_builder.apply_oncenamedepscomputeletmain()=letforce_semantic_folding=PropagationParameters.SemanticConstFolding.get()||not(Cil_datatype.Fundec.Set.is_empty(PropagationParameters.SemanticConstFold.get()))inifforce_semantic_foldingthencompute()let()=Db.Main.extendmain(*
Local Variables:
compile-command: "make -C ../../.."
End:
*)