123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497(** Autotuning of the configuration based on syntactic heuristics. *)openGobConfigopenGoblintCilopenAutoTune0(*Create maps that map each function to the ones called in it and the ones calling it
Only considers static calls!*)moduleFunctionSet=Set.Make(CilType.Varinfo)moduleFunctionCallMap=Map.Make(CilType.Varinfo)letaddOrCreateMapfd=function|Some(set,i)->Some(FunctionSet.addfdset,i+1)|None->Some(FunctionSet.singletonfd,1)classcollectFunctionCallsVisitor(callSet,calledBy,argLists,fd)=objectinheritnopCilVisitormethod!vinst=function|Call(_,Lval((Varinfo),NoOffset),args,_,_)->callSet:=FunctionSet.addinfo!callSet;calledBy:=FunctionCallMap.updateinfo(addOrCreateMapfd)!calledBy;(*We collect the argument list so we can use LibraryFunctions.find to classify functions*)argLists:=FunctionCallMap.addinfoargs!argLists;DoChildren|_->DoChildrenendclassfunctionVisitor(calling,calledBy,argLists,dynamicallyCalled)=objectinheritnopCilVisitormethod!vglob=function|GVarDecl(vinfo,_)->ifvinfo.vaddrof&&isFunctionTypevinfo.vtypethendynamicallyCalled:=FunctionSet.addvinfo!dynamicallyCalled;DoChildren|_->DoChildrenmethod!vfuncfd=letcallSet=refFunctionSet.emptyinletcallVisitor=newcollectFunctionCallsVisitor(callSet,calledBy,argLists,fd.svar)inignore@@Cil.visitCilFunctioncallVisitorfd;calling:=FunctionCallMap.addfd.svar!callSet!calling;DoChildrenendtypefunctionCallMaps={calling:FunctionSet.tFunctionCallMap.t;calledBy:(FunctionSet.t*int)FunctionCallMap.t;argLists:Cil.explistFunctionCallMap.t;dynamicallyCalled:FunctionSet.t;}letfunctionCallMaps=ResettableLazy.from_fun(fun()->letcalling=refFunctionCallMap.emptyinletcalledBy=refFunctionCallMap.emptyinletargLists=refFunctionCallMap.emptyinletdynamicallyCalled=refFunctionSet.emptyinletthisVisitor=newfunctionVisitor(calling,calledBy,argLists,dynamicallyCalled)invisitCilFileSameGlobalsthisVisitor(!Cilfacade.current_file);{calling=!calling;calledBy=!calledBy;argLists=!argLists;dynamicallyCalled=!dynamicallyCalled})(* Only considers static calls!*)letcalledFunctionsfd=(ResettableLazy.forcefunctionCallMaps).calling|>FunctionCallMap.find_optfd|>Option.value~default:FunctionSet.emptyletcallingFunctionsfd=(ResettableLazy.forcefunctionCallMaps).calledBy|>FunctionCallMap.find_optfd|>Option.value~default:(FunctionSet.empty,0)|>fstlettimesCalledfd=(ResettableLazy.forcefunctionCallMaps).calledBy|>FunctionCallMap.find_optfd|>Option.value~default:(FunctionSet.empty,0)|>sndletfunctionArgsfd=(ResettableLazy.forcefunctionCallMaps).argLists|>FunctionCallMap.find_optfdletfindMallocWrappers()=letisMallocf=ifLibraryFunctions.is_specialfthen(letdesc=LibraryFunctions.findfinmatchfunctionArgsfwith|None->false|Someargs->matchdesc.specialargswith|Malloc_->true|_->false)elsefalsein(ResettableLazy.forcefunctionCallMaps).calling|>FunctionCallMap.filter(fun_allCalled->FunctionSet.existsisMallocallCalled)|>FunctionCallMap.filter(funf_->timesCalledf>10)|>FunctionCallMap.bindings|>List.map(fun(v,_)->v.vname)|>List.iter(funn->print_endline("malloc wrapper: "^n);GobConfig.set_auto"ana.malloc.wrappers[+]"n)(*Functions for determining if the congruence analysis should be enabled *)letisExtern=function|Extern->true|_->falseletrecsetCongruenceRecursivefddepthneigbourFunction=ifdepth>=0then(fd.svar.vattr<-addAttributes(fd.svar.vattr)[Attr("goblint_precision",[AStr"congruence"])];FunctionSet.iter(funvinfo->print_endline(" "^vinfo.vname);setCongruenceRecursive(Cilfacade.find_varinfo_fundecvinfo)(depth-1)neigbourFunction)(FunctionSet.filter(*for extern and builtin functions there is no function definition in CIL*)(funx->not(isExternx.vstorage||BatString.starts_withx.vname"__builtin"))(neigbourFunctionfd.svar));)exceptionModFoundclassmodVisitor=objectinheritnopCilVisitormethod!vexpr=function|BinOp(Mod,_,_,_)->raiseModFound;|_->DoChildrenendclassmodFunctionAnnotatorVisitor=objectinheritnopCilVisitormethod!vfuncfd=letthisVisitor=newmodVisitorintryignore(visitCilFunctionthisVisitorfd)with|ModFound->print_endline("function "^(CilType.Fundec.showfd)^" uses mod, enable congruence domain recursively for:");print_endline(" \"down\":");setCongruenceRecursivefd6calledFunctions;print_endline(" \"up\":");setCongruenceRecursivefd3callingFunctions;;SkipChildrenendletaddModAttributesfile=set_bool"annotation.int.enabled"true;letthisVisitor=newmodFunctionAnnotatorVisitorinignore(visitCilFileSameGlobalsthisVisitorfile)letdisableIntervalContextsInRecursiveFunctions()=(ResettableLazy.forcefunctionCallMaps).calling|>FunctionCallMap.iter(funfset->(*detect direct recursion and recursion with one indirection*)ifFunctionSet.memfset||(not@@FunctionSet.disjoint(calledFunctionsf)(callingFunctionsf))then(print_endline("function "^(f.vname)^" is recursive, disable interval and interval_set contexts");f.vattr<-addAttributes(f.vattr)[Attr("goblint_context",[AStr"base.no-interval";AStr"base.no-interval_set";AStr"relation.no-context"])];))lethasFunctionpred=letrelevant_staticvar=ifLibraryFunctions.is_specialvarthenletdesc=LibraryFunctions.findvarinGobOption.exists(funargs->pred(desc.specialargs))(functionArgsvar)elsefalseinletrelevant_dynamicvar=ifLibraryFunctions.is_specialvarthenletdesc=LibraryFunctions.findvarin(* We don't really have arguments at hand, so we cheat and just feed it a list of MyCFG.unknown_exp of appropriate length *)matchunrollTypevar.vtypewith|TFun(_,args,_,_)->letargs=BatOption.map_default(List.map(fun(x,_,_)->MyCFG.unknown_exp))[]argsinpred(desc.specialargs)|_->falseelsefalseinletcalls=ResettableLazy.forcefunctionCallMapsincalls.calledBy|>FunctionCallMap.exists(funvar_->relevant_staticvar)||calls.dynamicallyCalled|>FunctionSet.existsrelevant_dynamicletdisableAnalysesanas=List.iter(GobConfig.set_auto"ana.activated[-]")anasletenableAnalysesanas=List.iter(GobConfig.set_auto"ana.activated[+]")anas(*If only one thread is used in the program, we can disable most thread analyses*)(*The exceptions are analyses that are depended on by others: base -> mutex -> mutexEvents, access*)(*escape is also still enabled, because otherwise we get a warning*)(*does not consider dynamic calls!*)letnotNeccessaryThreadAnalyses=["race";"deadlock";"maylocks";"symb_locks";"thread";"threadid";"threadJoins";"threadreturn"]letreduceThreadAnalyses()=letisThreadCreate=function|LibraryDesc.ThreadCreate_->true|_->falseinlethasThreadCreate=hasFunctionisThreadCreateinifnot@@hasThreadCreatethen(print_endline@@"no thread creation -> disabling thread analyses \""^(String.concat", "notNeccessaryThreadAnalyses)^"\"";disableAnalysesnotNeccessaryThreadAnalyses;)(* This is run independent of the autotuner being enabled or not to be sound in the presence of setjmp/longjmp *)(* It is done this way around to allow enabling some of these analyses also for programs without longjmp *)letlongjmpAnalyses=["activeLongjmp";"activeSetjmp";"taintPartialContexts";"modifiedSinceLongjmp";"poisonVariables";"expsplit";"vla"]letactivateLongjmpAnalysesWhenRequired()=letisLongjmp=function|LibraryDesc.Longjmp_->true|_->falseinifhasFunctionisLongjmpthen(print_endline@@"longjmp -> enabling longjmp analyses \""^(String.concat", "longjmpAnalyses)^"\"";enableAnalyseslongjmpAnalyses;)letfocusOnSpecification()=matchSvcomp.Specification.of_option()with|UnreachCalls->()|NoDataRace->(*enable all thread analyses*)print_endline@@"Specification: NoDataRace -> enabling thread analyses \""^(String.concat", "notNeccessaryThreadAnalyses)^"\"";enableAnalysesnotNeccessaryThreadAnalyses;|NoOverflow->(*We focus on integer analysis*)set_bool"ana.int.def_exc"true;set_bool"ana.int.interval"true(*Detect enumerations and enable the "ana.int.enums" option*)exceptionEnumFoundclassenumVisitor=objectinheritnopCilVisitormethod!vglob=function|GEnumTag_|GEnumTagDecl_->raiseEnumFound;|_->SkipChildren;endlethasEnumsfile=letthisVisitor=newenumVisitorintryignore(visitCilFileSameGlobalsthisVisitorfile);false;withEnumFound->trueclassaddTypeAttributeVisitor=objectinheritnopCilVisitor(*large arrays -> partitioned*)(*because unroll only is usefull if most values are actually unrolled*)method!vvdecinfo=(ifis_large_arrayinfo.vtype&¬@@hasAttribute"goblint_array_domain"(typeAttrsinfo.vtype)theninfo.vattr<-addAttribute(Attr("goblint_array_domain",[AStr"partitioned"]))info.vattr);DoChildren(*Set arrays with important types for thread analysis to unroll*)method!vtypetyp=letis_important_type(t:typ):bool=matchtwith|TNamed(info,attr)->List.meminfo.tname["pthread_mutex_t";"spinlock_t";"pthread_t"]|TInt(IInt,attr)->hasAttribute"mutex"attr|_->falseinifis_important_typetyp&¬@@hasAttribute"goblint_array_domain"(typeAttrstyp)thenChangeTo(typeAddAttributes[Attr("goblint_array_domain",[AStr"unroll"])]typ)elseSkipChildrenendletselectArrayDomainsfile=set_bool"annotation.goblint_array_domain"true;letthisVisitor=newaddTypeAttributeVisitorinignore(visitCilFileSameGlobalsthisVisitorfile)(*small unrolled loops also set domain of accessed arrays to unroll, at the point where loops are unrolled*)(*option that can be selected based on value/cost*)typeoption={value:int;cost:int;activate:unit->unit}(*Option for activating the octagon apron domain on selected vars*)moduleVariableMap=Map.Make(CilType.Varinfo)moduleVariableSet=Set.Make(CilType.Varinfo)letisComparison=function|Lt|Gt|Le|Ge|Ne|Eq->true|_->falseletisGoblintStubv=List.exists(fun(Attr(s,_))->s="goblint_stub")v.vattrletrecextractVar=function|UnOp(Neg,e,_)->extractVare|Lval((Varinfo),_)whennot(isGoblintStubinfo)->Someinfo|_->NoneletextractOctagonVars=function|BinOp(PlusA,e1,e2,(TInt_))|BinOp(MinusA,e1,e2,(TInt_))->(matchextractVare1,extractVare2with|Somea,Someb->Some(`Left(a,b))|Somea,None|None,Somea->ifisConstante1thenSome(`Righta)elseNone|_,_->None)|_->NoneletaddOrCreateVarMappingvarMapkeyvglobals=ifkey.vglob=globalsthenvarMap:=ifVariableMap.memkey!varMapthenletold=VariableMap.findkey!varMapinVariableMap.addkey(old+v)!varMapelseVariableMap.addkeyv!varMaplethandlevarMapvglobals=function|Some(`Left(a,b))->addOrCreateVarMappingvarMapavglobals;addOrCreateVarMappingvarMapbvglobals;|Some(`Righta)->addOrCreateVarMappingvarMapavglobals;|None->()classoctagonVariableVisitor(varMap,globals)=objectinheritnopCilVisitormethod!vexpr=function(*an expression of type +/- a +/- b where a,b are either variables or constants*)|BinOp(op,e1,e2,(TInt_))whenisComparisonop->(handlevarMap5globals(extractOctagonVarse1);handlevarMap5globals(extractOctagonVarse2);DoChildren)|Lval((Varinfo),_)whennot(isGoblintStubinfo)->handlevarMap1globals(Some(`Rightinfo));SkipChildren(*Traverse down only operations fitting for linear equations*)|UnOp(Neg,_,_)|BinOp(PlusA,_,_,_)|BinOp(MinusA,_,_,_)|BinOp(Mult,_,_,_)|BinOp(LAnd,_,_,_)|BinOp(LOr,_,_,_)->DoChildren|_->SkipChildrenendlettopVarsnvarMap=letcompareValueDesc=(fun(_,v1)(_,v2)->-(comparev1v2))invarMap|>VariableMap.bindings|>List.sortcompareValueDesc|>BatList.taken|>List.mapfstclassoctagonFunctionVisitor(list,amount)=objectinheritnopCilVisitormethod!vfuncf=letvarMap=refVariableMap.emptyinletvisitor=newoctagonVariableVisitor(varMap,false)inignore(visitCilFunctionvisitorf);list:=topVarsamount!varMap::!list;SkipChildrenendletcongruenceOptionfactorsfile=letlocals,globals=factors.integralVarsinletcost=(locals+globals)*(factors.instructions/12)+5*factors.functionCallsinletvalue=5*locals+globalsinletactivate()=print_endline@@"Congruence: "^string_of_intcost;set_bool"ana.int.congruence"true;print_endline"Enabled congruence domain.";in{value;cost;activate;}letapronOctagonOptionfactorsfile=letlocals=ifList.mem"specification"(get_string_list"ana.autotune.activated")&&get_string"ana.specification"<>""thenmatchSvcomp.Specification.of_option()with|NoOverflow->12|_->8else8inletglobals=2inletselectedLocals=letlist=ref[]inletvisitor=newoctagonFunctionVisitor(list,locals)invisitCilFileSameGlobalsvisitorfile;List.concat!listinletselectedGlobals=letvarMap=refVariableMap.emptyinletvisitor=newoctagonVariableVisitor(varMap,true)invisitCilFileSameGlobalsvisitorfile;topVarsglobals!varMapinletallVars=(selectedGlobals@selectedLocals)inletcost=(Batteries.Int.pow(locals+globals)3)*(factors.instructions/70)inletactivateVars()=print_endline@@"Octagon: "^string_of_intcost;set_bool"annotation.goblint_relation_track"true;set_string"ana.apron.domain""octagon";set_auto"ana.activated[+]""apron";set_bool"ana.apron.threshold_widening"true;set_string"ana.apron.threshold_widening_constants""comparisons";print_endline"Enabled octagon domain for:";print_endline@@String.concat", "@@List.map(funinfo->info.vname)allVars;List.iter(funinfo->info.vattr<-addAttribute(Attr("goblint_relation_track",[]))info.vattr)allVarsin{value=50*(List.lengthallVars);cost=cost;activate=activateVars;}letwideningOptionfactorsfile=letamountConsts=List.length@@WideningThresholds.upper_thresholds()inletcost=amountConsts*(factors.loops*5+factors.controlFlowStatements)in{value=amountConsts*(factors.loops*5+factors.controlFlowStatements);cost=cost;activate=fun()->print_endline@@"Widening: "^string_of_intcost;set_bool"ana.int.interval_threshold_widening"true;set_string"ana.int.interval_threshold_widening_constants""comparisons";print_endline"Enabled widening thresholds";}letestimateComplexityfactorsfile=letpathsEstimate=factors.loops+factors.controlFlowStatements/90inletoperationEstimate=factors.instructions+(factors.expressions/60)inletcallsEstimate=factors.functionCalls*factors.loops/factors.functions/10inletglobalVars=fstfactors.pointerVars*2+fstfactors.arrayVars*4+fstfactors.integralVarsinletlocalVars=sndfactors.pointerVars*2+sndfactors.arrayVars*4+sndfactors.integralVarsinletvarEstimates=globalVars+localVars/factors.functionsinpathsEstimate*operationEstimate*callsEstimate+varEstimates/10lettotalTarget=30000(*A simple greedy approximation to the knapsack problem:
take options with the highest use/cost ratio that still fit*)letchooseFromOptionscostTargetoptions=letratioo=Float.of_into.value/.Float.of_into.costinletcompareRatioo1o2=Float.compare(ratioo1)(ratioo2)inletrectakeFittingremainingTargetoptions=ifremainingTarget<0then(print_endline@@"Total: "^string_of_int(totalTarget-remainingTarget);[])elsematchoptionswith|o::os->ifo.cost<remainingTarget+costTarget/20then(*because we are already estimating, we allow overshooting *)o::takeFitting(remainingTarget-o.cost)oselsetakeFitting(remainingTarget-o.cost)os|[]->print_endline@@"Total: "^string_of_int(totalTarget-remainingTarget);[]intakeFittingcostTarget@@List.sortcompareRatiooptionsletisActivateda=get_bool"ana.autotune.enabled"&&List.mema@@get_string_list"ana.autotune.activated"letchooseConfigfile=letfactors=collectFactorsvisitCilFileSameGlobalsfileinletfileCompplexity=estimateComplexityfactorsfileinprint_endline"Collected factors:";printFactorsfactors;print_endline"";print_endline"Complexity estimates:";print_endline@@"File: "^string_of_intfileCompplexity;iffileCompplexity<totalTarget&&isActivated"congruence"thenaddModAttributesfile;ifisActivated"noRecursiveIntervals"thendisableIntervalContextsInRecursiveFunctions();ifisActivated"mallocWrappers"thenfindMallocWrappers();ifisActivated"specification"&&get_string"ana.specification"<>""thenfocusOnSpecification();ifisActivated"enums"&&hasEnumsfilethenset_bool"ana.int.enums"true;ifisActivated"singleThreaded"thenreduceThreadAnalyses();ifisActivated"arrayDomain"thenselectArrayDomainsfile;letoptions=[]inletoptions=ifisActivated"congruence"then(congruenceOptionfactorsfile)::optionselseoptionsinletoptions=ifisActivated"octagon"then(apronOctagonOptionfactorsfile)::optionselseoptionsinletoptions=ifisActivated"wideningThresholds"then(wideningOptionfactorsfile)::optionselseoptionsinList.iter(funo->o.activate())@@chooseFromOptions(totalTarget-fileCompplexity)optionsletreset_lazy()=ResettableLazy.resetfunctionCallMaps