123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384(**************************************************************************)(* *)(* SPDX-License-Identifier LGPL-2.1 *)(* Copyright (C) *)(* CEA (Commissariat à l'énergie atomique et aux énergies alternatives) *)(* *)(**************************************************************************)(** Transformation module. *)openCil_typesopenCilopenCabs(*-----------------------------------------------------------------------*)(* Transformation on Cabs unrolling loop conditions, identifying loop
bodies and adding unroll loop prammas *)moduleMark:sig(* returns [true] for an unprocessed loop body *)valis_loop_attr_markup:Cabs.statement->boolvalloop_attr_markup:int->is_unrollable:bool->Cabs.attributelist*boolend=structletloop_attr_name="acsl!loop_processed!"letattr_cabss={Cabs.expr_loc=Cabshelper.cabslu;Cabs.expr_node=Cabs.(CONSTANT(CONST_STRINGs))}letloop_attr_markup=letloop_condition_attr="acsl!loop_condition!unrolled"inletloop_processed_attr_cabs=[attr_cabsloop_attr_name]inletloop_condition_attr_cabs=attr_cabsloop_condition_attr::loop_processed_attr_cabsinfunn~is_unrollable->letloop_attr_cabs=ifis_unrollablethenloop_condition_attr_cabselseloop_processed_attr_cabsinletloop_attr_cabs=(attr_cabs(Paste.loop_body_attr_namen))::loop_attr_cabsin[Paste.hidden_attr,loop_attr_cabs],is_unrollableletis_block_markupprocessed_attrblock=not(List.exists(fun(attr,args)->((attr=Paste.hidden_attr)&&List.exists(function|{expr_node=CONSTANT(CONST_STRINGattr)}->processed_attr=attr|_->false)args))block.battrs)letis_stmt_markupprocessed_attr=function|{stmt_node=BLOCK(block,_,_)}->is_block_markupprocessed_attrblock|_->trueletis_loop_attr_markup=is_stmt_markuploop_attr_nameendlethas_unroll_loopli=List.existsLogic_ptree.(function|AExtended(_,_,{ext_name="unfold"})->true|_->false)lilethas_only_unroll_loopli=not(List.existsLogic_ptree.(function|AExtended(_,_,{ext_name="unfold"})->false|_->true)li)moduleS=structletstmtstmt_node~sref={srefwithstmt_node}letnop~loc=stmt(NOP(None,loc))letblock_node?(loc2=Cabshelper.cabslu)~locbstmtsbattrs=BLOCK({bstmts;blabels=[];battrs},loc,loc2)letblock?loc2~locbstmtsbattrs=stmt(block_node?loc2~locbstmtsbattrs)(* transform "{ ... L1: /*@annot*/ S1; ... }"
into "{ ... L1: {/*label attrib*/ /*@annot*/ S1; } ... }"
note: S1 cannot be a declaration because it is a labeled statement
returns None when the list is unmodified *)letget_processed_labelss=letis_annot=Cabs.(function|CODE_ANNOT_->true|CODE_SPEC_->true|_->false)inletmk_labels=function|LABEL(l,_,loc)->LABEL(l,s,loc)|CASERANGE(e1,e2,_,loc)->CASERANGE(e1,e2,s,loc)|CASE(e,_,loc)->CASE(e,s,loc)|DEFAULT(_,loc)->DEFAULT(s,loc)|_->assertfalseinletrecgetprev=function|({stmt_node=LABEL(_,s,loc)}assref)::ls|({stmt_node=DEFAULT(s,loc)}assref)::ls|({stmt_node=CASE(_,s,loc)}assref)::ls|({stmt_node=CASERANGE(_,_,s,loc)}assref)::ls->extractprev(sref,loc)[](s::ls)|s::ls->get(s::prev)ls|[]->prevandextractprevvreffs=function|s::lswhenis_annots.stmt_node->extractprevvref(s::fs)ls|s::ls->nextprevvref(s::fs)ls|[]->nextprevvreffs[]andnextprev(sref,loc)fsls=letbstmts=(List.revfs)inletblk=block~locbstmts[]~srefinletslabel=stmt(mk_labelblksref.stmt_node)~srefinget(slabel::prev)lsinletrecfind=function|({stmt_node=LABEL(_,s,loc)}assref)::ls|({stmt_node=DEFAULT(s,loc)}assref)::ls|({stmt_node=CASE(_,s,loc)}assref)::ls|({stmt_node=CASERANGE(_,_,s,loc)}assref)::ls->letrecfirst_rev_stmtsprev=function|s::_whens==sref->prev|s::ls->first_rev_stmts(s::prev)ls|[]->assertfalseinletprev=first_rev_stmts[]ssinSome(List.rev(extractprev(sref,loc)[](s::ls)))|_::ls->findls|[]->Noneinfindssend(** May add an UNROLL_LOOP pragma from -acsl-ulevel option *)letdkey=Options.register_category"trace-transformations"lettransform_cabscabs=(* Syntactic transformation of the source code transforming loop body:
- add a new attribute to blocks of each loop body,
- unrool loop conditions when -acsl-unroll-loop-conditions is set,
- insert unroll pragmas as specified by -acsl-ulevel option.
*)letunroll_loop_cond=Options.is_unroll_loop_condition_on()andunroll_loop_pragma=not(Options.is_unroll_loop_pragma_on())inletvisitor=object(self)inheritCabsvisit.nopCabsVisitorvalmutableloop_cpt=0valmutablefct_name=""(* Adds eventual unroll loop pragmas to the current loop statement [s] *)methodunroll_pragma_insertion_processs=letmk_speccst=Logic_ptree.({lexpr_loc=Cil_datatype.Location.unknown;lexpr_node=PLconstantcst})inifnotunroll_loop_pragmathenselsetrybeginletunroll_pragma_insertion_processloop_categoryli=Options.debug~level:3~dkey"Look at %S loop #%d of function %S for insertion of UNROLL pragma@."loop_categoryloop_cptfct_name;let(is_total_unrolling,nb_unrolling)=(* Raises [Not_found] if there is nothing to do. *)(* 1 - Check if there is no UNROLL_LOOP pragma for that loop *)ifhas_unroll_looplithenraiseNot_found;(* 2 - Check if there is unrolling level specified option for that
loop *)Options.find_ulevel_specloop_categoryloop_cptfct_name(* May raise [Not_found] *)inletunroll_specs=letunroll_specs=[mk_specLogic_ptree.(IntConstant(string_of_intnb_unrolling))]inifis_total_unrollingthen(mk_specLogic_ptree.(StringConstant"completely"))::unroll_specselseunroll_specsinletext=Logic_ptree.{ext_name="unfold";ext_plugin="kernel";ext_content=unroll_specs}inli@[Logic_ptree.(AExtended([],true,ext))]inmatchs.stmt_nodewith|WHILE(li,cond,body,loc)->letli=unroll_pragma_insertion_process"while"liin{swithstmt_node=WHILE(li,cond,body,loc)}|DOWHILE(li,cond,body,loc)->letli=unroll_pragma_insertion_process"do-while"liin{swithstmt_node=DOWHILE(li,cond,body,loc)}|FOR(li,init,cond,inc,body,loc)->letli=unroll_pragma_insertion_process"for"liin{swithstmt_node=FOR(li,init,cond,inc,body,loc)}|_->assertfalseendwithNot_found->smethodfresh_loop_body_attrli=letis_unrollable=unroll_loop_cond&&((has_only_unroll_loopli)||(Options.result"Loop condition of loop #%d of function %S was not unrolled since there is a loop annotation."loop_cptfct_name;false))inMark.loop_attr_markuploop_cpt~is_unrollable(* Returns a transformed loop where a fresh attribute is added to
each loop body. An attribute is also added when the loop
condition is unrolled. *)methodunroll_loop_process~sref=matchsref.stmt_nodewith|WHILE(li,cond,body,loc)->letbattrs,is_unrollable=self#fresh_loop_body_attrliinletbody=S.block~loc[body]battrs~srefinifis_unrollablethen(* Transforms the "while (c) Sb;" into
"if (c) do {/* body attribs */ Sb; } while (c);" *)letdowhile=S.stmt(DOWHILE(li,cond,body,loc))~srefandnop=S.nop~loc~srefinOptions.debug~level:2~dkey"Unrolling loop condition of loop #%d of function %S."loop_cptfct_name;IF(cond,dowhile,nop,loc)elseWHILE(li,cond,body,loc)|DOWHILE(li,cond,body,loc)->(* Transforms the "do Sb while (c);" into
"do {/* body attribs */ Sb; } while (c);" *)letbattrs,_=self#fresh_loop_body_attr[]inletbody=S.block~loc[body]battrs~srefinDOWHILE(li,cond,body,loc)|FOR(li,init,cond,inc,body,loc)->letbattrs,is_unrollable=self#fresh_loop_body_attrliinletmk_bodybs=S.block~loc(body::bs)battrs~srefinifis_unrollablethen(* Transforms the "for (s;c;e) Sb;" into
"{ s; if (c) do {/* body attribs */ Sb; e; } while (c); }" *)letbody=matchincwith|{expr_node=NOTHING}->mk_body[]|_->mk_body[S.stmt(COMPUTATION(inc,loc))~sref]inletdowhile_node=DOWHILE(li,cond,body,loc)inletifdowhile_node=matchcondwith|{expr_node=NOTHING}->(* ISO C11 : 6.8.5.3.2 *)letcond={condwithexpr_node=Cabs.(CONSTANT(CONST_INT"1"))}inDOWHILE(li,cond,body,loc)|_->letnop=S.nop~loc~srefinIF(cond,(S.stmtdowhile_node~sref),nop,loc)inOptions.debug~level:2~dkey"Unrolling loop condition of loop #%d of function %S."loop_cptfct_name;letinit=matchinitwith|FC_EXP{expr_node=NOTHING}->None|FC_EXPinit->Some(COMPUTATION(init,loc))|FC_DECLdecl->Some(DEFINITIONdecl)inmatchinitwith|None->ifdowhile_node|Someinit_node->letifdowhile=S.stmtifdowhile_node~srefinletinit=S.stmtinit_node~srefinS.block_node~loc[init;ifdowhile][]elseFOR(li,init,cond,inc,(mk_body[]),loc)|_->assertfalsemethod!vexpr_=SkipChildren(* share the AST via stmt such as
Return, IF, ... *)method!vinitexpr_=SkipChildren(* share the AST *)method!vtypespec_=SkipChildren(* share the AST *)method!vdecltype_=SkipChildren(* share the AST *)method!vname___=SkipChildren(* share the AST *)method!vspec_=SkipChildren(* share the AST via visitCilFunction *)method!vattr_=SkipChildren(* share the AST via Asm stmt *)method!vdefdef=matchdefwith|FUNDEF(_,(_,(name,_,_,_)),_,_,_)->beginloop_cpt<-0;fct_name<-name;DoChildrenend|_->SkipChildrenmethod!vblockblock=matchS.get_processed_labelblock.bstmtswith|None->DoChildren|Somebstmts->ChangeDoChildrenPost({blockwithbstmts},funx->x)method!vstmtsref=letchangef=ChangeDoChildrenPost([f~sref],funx->x)inletloop_process~sref=loop_cpt<-loop_cpt+1;letsref=self#unroll_pragma_insertion_processsrefinS.stmt(self#unroll_loop_process~sref)~srefinmatchsref.stmt_nodewith|WHILE(_,_,body,_)|DOWHILE(_,_,body,_)|FOR(_,_,_,_,body,_)whenMark.is_loop_attr_markupbody->changeloop_process|_->DoChildrenendinifunroll_loop_condthen(letrecget_basenamename=tryget_basename(Filename.chop_extensionname)withInvalid_argument_->nameinletcabsfile=Filepath.to_string(fstcabs)inletget_basename()=get_basename(Filename.basenamecabsfile)inOptions.debug~dkey"Unrolling loop conditions in file: %s ..."(get_basename()));ifunroll_loop_pragmathenbeginletrecget_basenamename=tryget_basename(Filename.chop_extensionname)withInvalid_argument_->nameinletcabsfile=Filepath.to_string(fstcabs)inletget_basename()=get_basename(Filename.basenamecabsfile)inOptions.debug~dkey"Inserting unrolling loop pragmas in file: %s ..."(get_basename())end;Cabsvisit.visitCabsFile(visitor:>Cabsvisit.cabsVisitor)cabs(*--------------------------*)let()=Frontc.add_syntactic_transformation(funcabs->ifOptions.is_importation_on()||(Options.is_unroll_loop_condition_on())||not(Options.is_unroll_loop_pragma_on())thentransform_cabscabselsecabs)(*-----------------------------------------------------------------------*)(* Transformation on Cil identifying loop body that must be done before
unrolling loops *)letident_attr_cilf_attr_namen=(Paste.hidden_attr,[AStr(f_attr_namen)])letloop_ident_attr_cil=ident_attr_cilPaste.loop_number_attr_nameletast_has_changed=reffalseclassdo_it=object(_self)inheritVisitor.frama_c_inplaceinitializerast_has_changed:=false;valmutableloop_cpt=0;valmutablecfg_has_changed=false;method!vfuncfundec=assert(notcfg_has_changed);loop_cpt<-0;letpost_goto_updater=(funid->ifcfg_has_changedthenbeginFile.must_recompute_cfgid;ast_has_changed:=true;cfg_has_changed<-falseend;id)inChangeDoChildrenPost(fundec,post_goto_updater)method!vstmt_auxs=matchs.skindwith|Loop(_,block,_,_,_)->loop_cpt<-1+loop_cpt;block.Cil_types.battrs<-Ast_attributes.add(loop_ident_attr_cilloop_cpt)block.Cil_types.battrs;DoChildren|_->DoChildrenendlettransform_cilfile=ifOptions.is_importation_on()thenletvisitor=newdo_itinVisitor.visitFramacFileSameGlobals(visitor:>Visitor.frama_c_visitor)file;if!ast_has_changedthenAst.mark_as_changed()let()=File.add_code_transformation_after_cleanup~deps:[(moduleOptions.Import:Parameter_sig.S);(moduleOptions.Run:Parameter_sig.S)]~before:[Unfold_loops.transform;Options.main_import]Options.aux_importtransform_cil(*-----------------------------------------------------------------------*)