123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814(**************************************************************************)(* *)(* 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_typesopenLocations(* Computation of over-approximated operational inputs:
An accurate computation of these inputs needs the computation of
under-approximated outputs.
*)typet=Inout_type.t={over_inputs:Locations.Zone.t;over_inputs_if_termination:Locations.Zone.t;over_logic_inputs:Locations.Zone.t;(* [over_logic_inputs] is used internally by Eva to make memexec consider also
the logic inputs of the function. Computed in [transfer_annotations]. *)under_outputs_if_termination:Locations.Zone.t;over_outputs:Locations.Zone.t;over_outputs_if_termination:Locations.Zone.t;}lettop={over_inputs=Zone.top;over_inputs_if_termination=Zone.top;over_logic_inputs=Zone.top;under_outputs_if_termination=Zone.bottom;over_outputs=Zone.top;over_outputs_if_termination=Zone.top;}(* [_if_termination] fields of the type above, which are the one propagated by
the dataflow analysis of this module. It is meaningless to store the other
ones, as they come from branches that are by construction not propagated
until the end by the dataflow. *)typecompute_t={over_inputs_d:Zone.t;under_outputs_d:Zone.t;over_outputs_d:Zone.t;}(* Initial value for the computation *)letempty={over_inputs_d=Zone.bottom;under_outputs_d=Zone.bottom;over_outputs_d=Zone.bottom;}letbottom={over_inputs_d=Zone.bottom;under_outputs_d=Zone.top;over_outputs_d=Zone.bottom;}letequalct1ct2=Zone.equalct1.over_inputs_dct2.over_inputs_d&&Zone.equalct1.under_outputs_dct2.under_outputs_d&&Zone.equalct1.over_outputs_dct2.over_outputs_dletjoinc1c2={over_inputs_d=Zone.joinc1.over_inputs_dc2.over_inputs_d;under_outputs_d=Zone.meetc1.under_outputs_dc2.under_outputs_d;over_outputs_d=Zone.joinc1.over_outputs_dc2.over_outputs_d;}letis_includedc1c2=Zone.is_includedc1.over_inputs_dc2.over_inputs_d&&Zone.is_includedc2.under_outputs_dc1.under_outputs_d&&Zone.is_includedc1.over_outputs_dc2.over_outputs_dletjoin_and_is_includedsmallerlarger=letjoin=joinsmallerlargerinjoin,equaljoinlarger;;letexternalize_zone~with_formalskf=Zone.filter_base(Eva.Logic_inout.accept_base~formals:with_formals~locals:falsekf)(* This code evaluates an assigns, computing in particular a sound approximation
of sure outputs. For an assigns [locs_out \from locs_from], the process
is the following:
- evaluate locs_out to locations; discard those that are not exact, as
we cannot guarantee that they are always assigned
- evaluate locs_from, as a zone (no need for locations)
- compute the difference between the out and the froms, ie remove the
zones that are such that [z \from z] holds
(Note: large parts of this code are inspired/redundant with
[assigns_to_zone_foobar_state] in Value/register.ml)
*)leteval_assignskfstateassigns=lettreat_one_zoneacc(out,fromsasasgn)=(* treat a single assign *)(* Return a list of independent output zones, plus a zone indicating
that the zone has been overwritten in a sure way *)letclean_deps=Locations.Zone.filter_base(function|Base.Var(v,_)|Base.Allocated(v,_,_)->not(Kernel_function.is_formalvkf)|Base.CLogic_Var_|Base.Null|Base.String_->true)inletout_term=out.it_contentinletoutputs_under,outputs_over,deps=ifLogic_const.(is_resultout_term||is_exit_statusout_term)then(Zone.bottom,Zone.bottom,Zone.bottom)elseletoutput=Eva.Logic_inout.assigns_tlval_to_zonesstateWriteout_terminmatchoutputwith|Someoutput->output.under,output.over,clean_depsoutput.deps|None->Inout_parameters.warning~current:true~once:true"failed to interpret assigns clause '%a'"Printer.pp_termout_term;(Zone.bottom,Zone.top,Zone.top)in(* Compute all inputs as a zone *)letinputs=matchfromswith|FromAny->Zone.top|Froml->letauxacc{it_content=from}=letinputs=Eva.Logic_inout.assigns_tlval_to_zonesstateReadfrominmatchinputswith|Someinputs->letacc=Zone.join(clean_depsinputs.deps)accinZone.joininputs.overacc|_->Inout_parameters.warning~current:true~once:true"failed to interpret inputs in assigns clause '%a'"Printer.pp_fromasgn;Zone.topinList.fold_leftauxdepslin(* Fuse all outputs. An output is sure if it was certainly
overwritten (i.e. is in the left part of an assign clause,
and if it is not amongst its from.) *)(* Note: here we remove an overapproximation from an
underapproximation to get an underapproximation, which is not
the usual direction. It works here because diff on non-top zones is
an exact operation. *)letsure_out=Zone.(ifequaltopinputsthenbottomelsediffoutputs_underinputs)in{under_outputs_d=Zone.linkacc.under_outputs_dsure_out;over_inputs_d=Zone.joinacc.over_inputs_dinputs;over_outputs_d=Zone.joinacc.over_outputs_doutputs_over;}inmatchassignswith|WritesAny->Inout_parameters.warning"@[no assigns clauses for@ function %a.@]@ \
Results will be imprecise."Kernel_function.prettykf;top|Writesl->letinit={bottomwithunder_outputs_d=Zone.bottom}inletr=List.fold_lefttreat_one_zoneinitlin{over_inputs=r.over_inputs_d;over_logic_inputs=r.over_inputs_d;over_inputs_if_termination=r.over_inputs_d;under_outputs_if_termination=r.under_outputs_d;over_outputs=r.over_outputs_d;over_outputs_if_termination=r.over_outputs_d;}letcompute_using_specstatekf=letbehaviors=Eva.Logic_inout.valid_behaviorskfstateinletassigns=Ast_info.merge_assignsbehaviorsineval_assignskfstateassignsletcompute_using_prototype?stmtkf=letstate=Cumulative_analysis.specialize_state_on_call?stmtkfincompute_using_specstatekf(* Results of this module, consolidated by functions. Formals and locals
are stored *)moduleInternals=Kernel_function.Make_Table(Inout_type)(structletname="Inout.Operational_inputs.Internals"letdependencies=[Eva.Analysis.self]letsize=17end)moduleCallsite=Datatype.Pair_with_collections(Kernel_function)(Cil_datatype.Kinstr)(structletmodule_name="From.Callsite"end)moduleCallsiteHash=Callsite.Hashtbl(* Results of an an entire call, represented by a pair (stmt, kernel_function).
*)moduleCallwiseResults=State_builder.Hashtbl(Callsite.Hashtbl)(Inout_type)(structletsize=17letdependencies=[Internals.self]letname="Inout.Operational_inputs.CallwiseResults"end)moduleComputer(Fenv:Dataflows.FUNCTION_ENV)(X:sigval_version:string(* Debug: Callwise or functionwise *)val_kf:kernel_function(* Debug: Function being analyzed *)valkf_pre_state:Cvalue.Model.t(* Memory pre-state of the function. *)valstmt_state:stmt->Cvalue.Model.t(* Memory state at the given stmt *)valstmt_request:stmt->Eva.Results.request(* Request at the given stmt *)valat_call:stmt->kernel_function->Inout_type.t(* Results of the
analysis for the given call. Must not contain locals or formals *)end)=struct(* We want to compute the in/out for all terminating and
non-terminating points of the function. This is not immediate
with a dataflow, as all (1) infinite loops, (2) branches that call a non
terminating function, or (3) branches that fail, will not appear in the
final state. Hence, we two use auxiliary variables into which we add
all partial results. *)letnon_terminating_inputs=refZone.bottomletnon_terminating_outputs=refZone.bottomletnon_terminating_logic_inputs=refZone.bottomletstore_non_terminating_inputsinputs=non_terminating_inputs:=Zone.join!non_terminating_inputsinputs;;;letstore_non_terminating_logic_inputslogic_inputs=non_terminating_logic_inputs:=Zone.join!non_terminating_logic_inputslogic_inputsletstore_non_terminating_outputsoutputs=non_terminating_outputs:=Zone.join!non_terminating_outputsoutputs;;;(* Store the 'non-termination' information of a function subcall into
the current call. [under_outputs] are the current call sure outputs. *)letstore_non_terminating_subcallunder_outputssubcall=store_non_terminating_inputs(Zone.diffsubcall.over_inputsunder_outputs);store_non_terminating_outputssubcall.over_outputs;;;letcatenatec1c2=letinputs=Zone.diffc2.over_inputs_dc1.under_outputs_dinstore_non_terminating_inputsinputs;{over_inputs_d=Zone.joinc1.over_inputs_dinputs;under_outputs_d=Zone.linkc1.under_outputs_dc2.under_outputs_d;over_outputs_d=Zone.joinc1.over_outputs_dc2.over_outputs_d;}typet=compute_tletprettyfmtx=Format.fprintffmt"@[Over-approximated operational inputs: %a@]@\n\
@[Under-approximated operational outputs: %a@]"Zone.prettyx.over_inputs_dZone.prettyx.under_outputs_dletbottom=bottomletjoin_and_is_included=join_and_is_includedletjoin=joinletis_included=is_included(* Transfer function on expression. *)lettransfer_expsexpdata=letrequest=X.stmt_requestsinletinputs=Eva.Results.expr_depsexprequestinletnew_inputs=Zone.diffinputsdata.under_outputs_dinstore_non_terminating_inputsnew_inputs;{datawithover_inputs_d=Zone.joindata.over_inputs_dnew_inputs}(* Initialized const variables should be included as outputs of the function,
so [for_writing] must be false for local initializations. It should be
true for all other instructions. *)letadd_out~for_writingrequestlvdepsdata=letlv_address=Eva.Results.eval_address~for_writinglvrequestinletnew_outs=Eva.Results.as_zonelv_addressinletexact=Eva.Results.is_singletonlv_addressinstore_non_terminating_outputsnew_outs;letlv_deps=Eva.Results.address_depslvrequestinletdeps=Zone.joinlv_depsdepsinletnew_inputs=Zone.diffdepsdata.under_outputs_dinstore_non_terminating_inputsnew_inputs;letnew_sure_outs=ifexactthen(* There is only one modified zone. So, this is an exact output.
Add it into the under-approximated outputs. *)Zone.linkdata.under_outputs_dnew_outselsedata.under_outputs_din{under_outputs_d=new_sure_outs;over_inputs_d=Zone.joindata.over_inputs_dnew_inputs;over_outputs_d=Zone.joindata.over_outputs_dnew_outs}lettransfer_call~for_writingsdestfargs_locdata=letrequest=X.stmt_requestsin(* Join the inputs of [args] and of the function expression. *)leteval_depsacce=Zone.joinacc(Eva.Results.expr_depserequest)inletf_args_inputs=List.fold_lefteval_depsZone.bottom(f::args)inletdata=catenatedata{over_inputs_d=f_args_inputs;under_outputs_d=Zone.bottom;over_outputs_d=Zone.bottom;}inletcalled=Eva.Results.(eval_calleefrequest|>default[])inletfor_functions=List.fold_left(funacckf->letres=X.at_callskfinstore_non_terminating_subcalldata.over_outputs_dres;letfor_function={over_inputs_d=res.over_inputs_if_termination;under_outputs_d=res.under_outputs_if_termination;over_outputs_d=res.over_outputs_if_termination;}injoinfor_functionacc)bottomcalledinletresult=catenatedatafor_functionsinletresult=(* Treatment for the possible assignment of the call result *)(matchdestwith|None->result|Somelv->add_out~for_writingrequestlvZone.bottomresult)inresult(* Propagate all zones in predicates for the given statement, only in the case
of assertions and loop-invariants. For the time being, we do not treat
terminating and non-terminating points of the function differently. *)lettransfer_annotationsstmt=Annotations.iter_code_annot(fun_ca->matchca.annot_contentwith|AAssert(_,p)|AInvariant(_,true,p)->beginletpre=X.kf_pre_stateandhere=X.stmt_statestmtinletdeps=Eva.Logic_inout.predicate_deps~pre~herep.tp_statementinmatchdepswith|None->(* To be sound, we should perform a join with the top zone here.
We do nothing instead because the latter behavior would
directly disable memexec. *)()|Somep_zone->store_non_terminating_logic_inputsp_zoneend|_->())stmt(* Transfer function on instructions. *)lettransfer_instrstmt(i:instr)(data:t)=matchiwith|Set(lv,exp,_)->letrequest=X.stmt_requeststmtinlete_inputs=Eva.Results.expr_depsexprequestinadd_out~for_writing:truerequestlve_inputsdata|Local_init(v,AssignIniti,_)->letrequest=X.stmt_requeststmtinletrecauxlviacc=matchiwith|SingleInite->lete_inputs=Eva.Results.expr_depserequestinadd_out~for_writing:falserequestlve_inputsacc|CompoundInit(ct,initl)->(* Avoid folding implicit zero-initializer of large arrays. *)letimplicit=Cumulative_analysis.fold_implicit_initializerctinletdoinitoi_data=aux(Cil.addOffsetLvalolv)idatainletdata=Cil.foldLeftCompound~implicit~doinit~ct~initl~accinifimplicitthendataelse(* If the implicit zero-initializers hade been skipped, add the
zone of the array as outputs. It is exactly the written zone for
arrays of scalar elements. Nothing is read by zero-initializers,
so the inputs are empty. *)add_out~for_writing:falserequestlvZone.bottomaccinaux(Cil.varv)idata|Call(lvaloption,funcexp,argl,loc)->transfer_call~for_writing:truestmtlvaloptionfuncexpargllocdata|Local_init(v,ConsInit(f,args,kind),loc)->lettransfer=transfer_call~for_writing:falsestmtinCil.treat_constructor_as_functransfervfargskindlocdata|Asm_|Code_annot_|Skip_->data;;(* transfer_guard: gets the state obtained after evaluating the
condition, and split the state according to the truth value of
the condition. In this case, we just make sure that dead
edges get bottom, instead of the input state. *)lettransfer_guardstmtet=letrequest=X.stmt_requeststmtinletv_e=Eva.Results.(eval_experequest|>as_cvalue)inlett1=Cil.unrollType(Cil.typeOfe)inletdo_then,do_else=ifCil.isIntegralTypet1||Cil.isPointerTypet1thenCvalue.V.contains_non_zerov_e,Cvalue.V.contains_zerov_eelsetrue,true(* TODO: a float condition is true iff != 0.0 *)in(ifdo_thenthentelsebottom),(ifdo_elsethentelsebottom);;letreturn_data=refbottom;;lettransfer_stmtsdata=letmap_on_all_succsnew_data=List.map(funx->(x,new_data))s.succsinmatchs.skindwith|Instri->map_on_all_succs(transfer_instrsidata)|If(exp,_,_,_)->letdata=transfer_expsexpdatainDataflows.transfer_if_from_guardtransfer_guardsdata|Switch(exp,_,_,_)->letdata=transfer_expsexpdatainDataflows.transfer_switch_from_guardtransfer_guardsdata|Return(Someexp,_)->return_data:=transfer_expsexpdata;assert(s.succs==[]);[]|Return(None,_)->return_data:=data;assert(s.succs==[]);[]|Throw_|TryCatch_->Inout_parameters.fatal"Exception node in the AST"|UnspecifiedSequence_|Loop_|Block_|Goto_|Break_|Continue_|TryExcept_|TryFinally_->map_on_all_succsdata;;lettransfer_stmtsdata=ifCvalue.Model.is_reachable(X.stmt_states)thenbegintransfer_annotationss;transfer_stmtsdataendelse[];;letinit=[(Kernel_function.find_first_stmtFenv.kf),empty];;letend_dataflow()=letres_if_termination=!return_datain{over_inputs_if_termination=res_if_termination.over_inputs_d;under_outputs_if_termination=res_if_termination.under_outputs_d;over_outputs_if_termination=res_if_termination.over_outputs_d;over_inputs=Zone.join!non_terminating_inputsres_if_termination.over_inputs_d;over_logic_inputs=!non_terminating_logic_inputs;over_outputs=Zone.join!non_terminating_outputsres_if_termination.over_outputs_d;}endletexternalize~with_formalskfv=letfilter=externalize_zone~with_formalskfinInout_type.mapfiltervletcompute_externals_using_prototype?stmtkf=letinternals=compute_using_prototype?stmtkfinexternalize~with_formals:falsekfinternalsletget_internal_aux?stmtkf=matchstmtwith|None->!Db.Operational_inputs.get_internalkf|Somestmt->tryCallwiseResults.find(kf,Kstmtstmt)withNot_found->ifEva.Analysis.use_spec_instead_of_definitionkfthencompute_using_prototype~stmtkfelse!Db.Operational_inputs.get_internalkfletget_external_aux?stmtkf=matchstmtwith|None->!Db.Operational_inputs.get_externalkf|Somestmt->tryletinternals=CallwiseResults.find(kf,Kstmtstmt)inexternalize~with_formals:falsekfinternalswithNot_found->ifEva.Analysis.use_spec_instead_of_definitionkfthenletr=compute_externals_using_prototype~stmtkfinCallwiseResults.add(kf,Kstmtstmt)r;relse!Db.Operational_inputs.get_externalkfletextract_inout_from_fromsfroms=letopenFunction_Fromsinlet{deps_return;deps_table}=fromsinletin_return=Deps.to_zonedeps_returninletin_,out_=matchdeps_tablewith|Memory.Top->Zone.top,Zone.top|Memory.Bottom->Zone.bottom,Zone.bottom|Memory.Mapm->letaux_fromoutin_(acc_in,acc_outasacc)=letopenDepsOrUnassignedin(* Skip zones fully unassigned, they are not really port of the
dependencies, but just present in the offsetmap to avoid "holes" *)matchin_with|DepsBottom|Unassigned->acc|AssignedFromin_|MaybeAssignedFromin_->Zone.joinacc_in(Deps.to_zonein_),Zone.joinacc_outoutinMemory.foldaux_fromm(Zone.bottom,Zone.bottom)in(Zone.joinin_returnin_),out_[@@@warning"-60"]moduleCallwise=structletmerge_call_in_local_tablecalllocal_tablev=letprev=tryCallsiteHash.findlocal_tablecallwithNot_found->Inout_type.bottominletjoined=Inout_type.joinvprevinCallsiteHash.replacelocal_tablecalljoinedletmerge_call_in_global_tables(kf,_ascall)v=(* Global callwise table *)letprev=tryCallwiseResults.findcallwithNot_found->Inout_type.bottominCallwiseResults.replacecall(Inout_type.joinvprev);(* Global, kf-indexed, table *)letprev=tryInternals.findkfwithNot_found->Inout_type.bottominInternals.replacekf(Inout_type.joinvprev);;;letcall_inout_stack=ref[]letcall_for_callwise_inout_callstackkf_state=function|`Body->lettable_current_function=CallsiteHash.create7incall_inout_stack:=(kf,table_current_function)::!call_inout_stack|`Reuse|`Spec|`Builtin->()letpop_local_tablekf=match!call_inout_stackwith|(kf',table)::tail->ifnot(Kernel_function.equalkfkf')thenInout_parameters.fatal"callwise inout: %a != %a@."Kernel_function.prettykfKernel_function.prettykf';CallsiteHash.itermerge_call_in_global_tablestable;call_inout_stack:=tail;|[]->Inout_parameters.fatal"callwise: internal stack is empty"letend_recordcallstackkfinout=Db.Operational_inputs.Record_Inout_Callbacks.applyinout;letcallsite=Eva.Callstack.top_callsitecallstackinmatchcallsite,!call_inout_stackwith|Kstmt_,(_caller,table)::_->merge_call_in_local_table(kf,callsite)tableinout;|Kglobal,[]->(* the entry point *)merge_call_in_global_tables(kf,callsite)inout;CallwiseResults.mark_as_computed()|_->Inout_parameters.fatal"callwise: internal stack is inconsistent with Eva callstack"moduleMemExec=State_builder.Hashtbl(Datatype.Int.Hashtbl)(Inout_type)(structletsize=17letdependencies=[Internals.self]letname="Operational_inputs.MemExec"end)letcompute_call_from_value_stateskfcall_stackstates=letmoduleFenv=(valDataflows.function_envkf:Dataflows.FUNCTION_ENV)inletmoduleComputer=Computer(Fenv)(structlet_version="callwise"let_kf=kf(* Returns the [kf] pre-state with respect to the single [call_stack]. *)letkf_pre_state=Eva.Results.(at_start_ofkf|>in_callstackcall_stack|>get_cvalue_model)letstmt_statestmt=tryCil_datatype.Stmt.Hashtbl.findstatesstmtwithNot_found->Cvalue.Model.bottomletstmt_requeststmt=Eva.Results.in_cvalue_state(stmt_statestmt)letat_callstmtkf=let_cur_kf,table=List.hd!call_inout_stackintryletwith_internals=CallsiteHash.findtable(kf,Kstmtstmt)inletfilter=matchkf.fundecwith|Definition(fundec,_)->(funb->not(Base.is_formal_or_localbfundec))|_->letvi_kf=Kernel_function.get_vikfin(funb->not(Base.is_formal_of_prototypebvi_kf))inInout_type.map(Zone.filter_basefilter)with_internalswithNot_found->Inout_type.bottomend)inletmodule[@warning"-60"]Compute=Dataflows.Simple_forward(Fenv)(Computer)inComputer.end_dataflow()letrecord_for_callwise_inoutcallstackkfpre_statevalue_res=letinout=matchvalue_reswith|`Body(Eva.Cvalue_callbacks.{before_stmts},memexec_counter)->letinout=ifEva.Analysis.save_resultskfthenletcvalue_states=Lazy.forcebefore_stmtsincompute_call_from_value_stateskfcallstackcvalue_stateselsetopinMemExec.replacememexec_counterinout;pop_local_tablekf;inout|`Reusecounter->MemExec.findcounter|`Spec_states|`Builtin(_states,None)->compute_using_specpre_statekf|`Builtin(_states,Some(froms,sure_out))->letin_,out_=extract_inout_from_fromsfromsin{over_inputs_if_termination=in_;over_inputs=in_;over_logic_inputs=Zone.bottom;over_outputs_if_termination=out_;over_outputs=out_;under_outputs_if_termination=sure_out;}inend_recordcallstackkfinout(* Register our callbacks inside the value analysis *)let()=Eva.Cvalue_callbacks.register_call_results_hookrecord_for_callwise_inout;Eva.Cvalue_callbacks.register_call_hookcall_for_callwise_inoutend(* Functionwise version of the computations. *)moduleFunctionWise=struct(* Stack of function being processed *)letcall_stack:kernel_functionStack.t=Stack.create()letcompute_internal_using_cfgkf=tryletmoduleFenv=(valDataflows.function_envkf:Dataflows.FUNCTION_ENV)inletmoduleComputer=Computer(Fenv)(structlet_version="functionwise"let_kf=kfletkf_pre_state=Eva.Results.(at_start_ofkf|>get_cvalue_model)letstmt_states=Eva.Results.(befores|>get_cvalue_model)letstmt_requests=Eva.Results.beforesletat_callstmtkf=get_external_aux~stmtkfend)inStack.iter(fung->ifkf==gthenraiseExit)call_stack;Stack.pushkfcall_stack;letmodule[@warning"-60"]Compute=Dataflows.Simple_forward(Fenv)(Computer)inletresult=Computer.end_dataflow()inignore(Stack.popcall_stack);resultwithExit->Inout_type.bottom(*TODO*)(*{
Inout_type.over_inputs_if_termination = empty.over_inputs_d ;
under_outputs_if_termination = empty.under_outputs_d;
over_inputs = empty.over_inputs_d;
over_outputs = empty.over_outputs_d;
over_outputs_if_termination = empty.over_outputs_d;
}*)letcompute_internal_using_cfgkf=ifnot(Eva.Analysis.save_resultskf)thentopelsebeginInout_parameters.feedback~level:2"computing for function %a%s"Kernel_function.prettykf(lets=ref""inStack.iter(funkf->s:=!s^" <-"^(Format.asprintf"%a"Kernel_function.prettykf))call_stack;!s);letr=compute_internal_using_cfgkfinInout_parameters.feedback~level:2"done for function %a"Kernel_function.prettykf;rendendletget_internal=Internals.memo(funkf->Eva.Analysis.compute();tryInternals.findkf(* The results may have been computed by the call
to Eva.Analysis.compute *)with|Not_found->ifEva.Analysis.use_spec_instead_of_definitionkfthencompute_using_prototypekfelseFunctionWise.compute_internal_using_cfgkf)letraw_externals~with_formalskf=letfilter=externalize~with_formalskfinfilter(get_internalkf)moduleExternals=Kernel_function.Make_Table(Inout_type)(structletname="External inouts full"letdependencies=[Internals.self]letsize=17end)letget_external=Externals.memo(raw_externals~with_formals:false)letcompute_externalkf=ignore(get_externalkf)moduleExternals_With_Formals=Kernel_function.Make_Table(Inout_type)(structletname="Inout.Operational_inputs.Externals_With_Formals"letdependencies=[Internals.self]letsize=17end)letget_external_with_formals=Externals_With_Formals.memo(raw_externals~with_formals:true)letpretty_operational_inputs_internalfmtkf=Format.fprintffmt"@[InOut (internal) for function %a:@\n%a@]@\n"Kernel_function.prettykfInout_type.pretty_operational_inputs(get_internalkf)letpretty_operational_inputs_externalfmtkf=Format.fprintffmt"@[InOut for function %a:@\n%a@]@\n"Kernel_function.prettykfInout_type.pretty_operational_inputs(get_externalkf)letpretty_operational_inputs_external_with_formalsfmtkf=Format.fprintffmt"@[InOut (with formals) for function %a:@\n%a@]@\n"Kernel_function.prettykfInout_type.pretty_operational_inputs(get_external_with_formalskf)let()=Db.Operational_inputs.self_internal:=Internals.self;Db.Operational_inputs.self_external:=Externals.self;Db.Operational_inputs.get_internal:=get_internal;Db.Operational_inputs.get_external:=get_external;Db.Operational_inputs.get_internal_precise:=get_internal_aux;Db.Operational_inputs.compute:=compute_external;Db.Operational_inputs.display:=pretty_operational_inputs_internal(*
Local Variables:
compile-command: "make -C ../../.."
End:
*)