Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Source file asl_utils.ml
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525(****************************************************************
* ASL utility functions
*
* Copyright Arm Limited (c) 2017-2019
* SPDX-Licence-Identifier: BSD-3-Clause
****************************************************************)(** ASL utility functions *)modulePP=Asl_parser_ppmoduleAST=Asl_astopenASTopenAsl_visitor(****************************************************************)(** {2 Bindings and IdentSet} *)(****************************************************************)(** {2 Bindings: maps indexed by identifiers} *)moduleBindings=Map.Make(AST.Id)(** add association list to bindings *)letadd_bindings(bs:'aBindings.t)(xs:(ident*'a)list):'aBindings.t=List.fold_left(funa(k,v)->Bindings.addkva)bsxs(** create bindings from association list *)letmk_bindings(xs:(ident*'a)list):'aBindings.t=add_bindingsBindings.emptyxs(** print bindings *)letpp_bindings(pp:'a->string)(bs:'aBindings.t):string=String.concat", "(List.map(fun(k,v)->pprint_identk^"->"^ppv)(Bindings.bindingsbs))(** {2 Sets of identifiers} *)moduleIdentSet=Set.Make(Id)(** merge a list of sets *)letunionSets(idss:IdentSet.tlist):IdentSet.t=List.fold_leftIdentSet.unionIdentSet.emptyidss(** add v to set of identifiers mapped to k *)letaddToBindingSet(k:ident)(v:ident)(bs:IdentSet.tBindings.t):IdentSet.tBindings.t=Bindings.updatek(funold->(matcholdwith|None->Some(IdentSet.singletonv)|Somevs->Some(IdentSet.addvvs)))bs(** convert identifier set to sorted list of identifiers
The implementation is trivial and exists mostly to emphasize that the
resulting list is sorted
*)letto_sorted_list(s:IdentSet.t):identlist=IdentSet.elementss(****************************************************************)(** {2 Equivalence classes} *)(****************************************************************)(** Equivalence classes are represented by trees.
The root of the tree is the canonical member of the class.
Traversing the parent node takes you closer to the canonical member.
The root is its own parent.
*)typetree={mutableparent:tree;data:ident;}(** Equivalence class support (to support unification, and similar)
The implementation is based on
{{:https://en.wikipedia.org/wiki/Disjoint-set_data_structure}Wikipedia: Union-Find}.
I have not implemented all the optimizations they suggest
because I expect sets to be quite small in practice.
*)classequivalences=object(self)(* Mapping from elements to the set containing them *)valmutableforest:treeBindings.t=Bindings.empty(* Find the root (canonical member of) the set.
* Implements "path-splitting" optimisation that makes every node
* point to its grandfather so each traversal reduces height of tree.
*)methodprivatefind(x:tree):tree=letr=refxinwhile!r.parent!=!rdoletnext=!r.parentin!r.parent<-next.parent;r:=nextdone;!r(* Find the root of the set containing 'x' - creating a new
* set if not already known *)methodprivatefind_ident(x:ident):tree=lets=(matchBindings.find_optxforestwith|None->letrect={parent=t;data=x;}int|Somet->self#findt)inforest<-Bindings.addxsforest;s(* Find the canonical member of the set containing 'x' *)methodcanonicalize(x:ident):ident=lets=self#find_identxins.data(* Merge the sets containing 'x' and 'y' *)methodmerge(x:ident)(y:ident):unit=letx'=self#find_identxinlety'=self#find_identyinifx!=ytheny'.parent<-x'(* Optimization: short circuit every tree so that they all point directly at root *)methodprivatenormalize:unit=forest<-Bindings.map(self#find)forest(* Return mapping from identifiers to the canonical representation of their
* equivalence class
*)methodmapping:identBindings.t=self#normalize;Bindings.map(funt->(self#findt).data)forest(* Construct equivalence classes for each canonical member of a class.
*
* The implementation of this could be made more efficient by adding
* pointers to trees so that we can map each canonical member to a
* tree containing all the nodes that point to it.
* But this implementation just does a linear scan over all the members
* of the forest.
*)methodclasses:IdentSet.tBindings.t=Bindings.fold(funkv->addToBindingSetvk)self#mappingBindings.empty(* Print equivalence classes adding a prefix at the start of every line of
* output.
*)methodpp(prefix:string):unit=Bindings.iter(funvvs->Printf.printf"%s%s -> {"prefix(pprint_identv);IdentSet.iter(funw->Printf.printf" %s"(pprint_identw))vs;Printf.printf"}\n";)self#classesend(****************************************************************)(** {1 AST Transformation Utilities} *)(****************************************************************)(****************************************************************)(** {2 Calculating free variables of expressions and types} *)(****************************************************************)classfreevarClass=objectinheritnopAslVisitorvalmutablefvs=IdentSet.emptymethodresult=fvsmethod!vvarx=fvs<-IdentSet.addxfvs;SkipChildrenmethod!vtypety=matchtywith|Type_Register_->(* Free variables in register types are not supported and will
lead to a type error.
Uses of global constants and variables in the indices of field
declarations of a register type are allowed, though, and will
be checked by the type checker as usual. Note that they will
not be evaluated at register declaration time, but every time
the respective register field is accessed (the type checker
desugars register field accesses to slice expressions, copying
the field indices). *)SkipChildren|_->DoChildrenendletfv_expr(x:expr):IdentSet.t=letfv=newfreevarClassinignore(visit_expr(fv:>aslVisitor)x);fv#resultletfv_type(x:ty):IdentSet.t=letfv=newfreevarClassinignore(visit_type(fv:>aslVisitor)x);fv#resultletfv_args(atys:(ty*ident)list):IdentSet.t=unionSets(List.map(fun(ty,_)->fv_typety)atys)letfv_sformal(x:sformal):IdentSet.t=(matchxwith|Formal_In(ty,v)->fv_typety|Formal_InOut(ty,v)->fv_typety)letfv_sformals(atys:sformallist):IdentSet.t=unionSets(List.mapfv_sformalatys)letfv_stmtsstmts=letfvs=newfreevarClassinignore(visit_stmts(fvs:>aslVisitor)stmts);fvs#resultletfv_decldecl=letfvs=newfreevarClassinignore(visit_decl(fvs:>aslVisitor)decl);fvs#result(****************************************************************)(** {2 Calculating assigned variables in statements} *)(****************************************************************)classassignedVarsClass=objectinheritnopAslVisitorvalmutableavs=IdentSet.emptymethodresult=avsmethod!vlvarx=avs<-IdentSet.addxavs;SkipChildrenendletassigned_vars_of_stmtsstmts=letavs=newassignedVarsClassinignore(visit_stmts(avs:>aslVisitor)stmts);avs#resultletassigned_vars_of_decldecl=letavs=newassignedVarsClassinignore(visit_decl(avs:>aslVisitor)decl);avs#result(****************************************************************)(** {2 Collect local bindings (variables and constants)} *)(****************************************************************)classlocalsClass=object(self)inheritnopAslVisitorvalmutablestack=[(Bindings.empty:tyBindings.t)]methodlocals=letmerge_xy=SomexinList.fold_right(Bindings.unionmerge)stackBindings.emptymethodadd_local(ty,id)=matchstackwith|s::ss->stack<-(Bindings.addidtys::ss)|[]->failwith"addLocal: empty stack"method!enter_scopevars=stack<-Bindings.empty::stack;List.iterself#add_localvarsmethod!leave_scope()=matchstackwith|s::ss->stack<-ss|[]->failwith"leave_scope: empty stack"method!vstmt=function|Stmt_VarDecl(ty,id,_,_)|Stmt_ConstDecl(ty,id,_,_)->self#add_local(ty,id);DoChildren|Stmt_VarDeclsNoInit(ty,ids,_)->List.iter(funid->self#add_local(ty,id))ids;DoChildren|_->DoChildrenendletlocals_of_stmtsstmts=letlc=newlocalsClassinignore(Visitor.mapNoCopy(visit_stmt(lc:>aslVisitor))stmts);lc#localsletlocals_of_decldecl=letlc=newlocalsClassinignore(Visitor.mapNoCopy(visit_decl(lc:>aslVisitor))decl);lc#locals(****************************************************************)(** {2 Calculate types used in expressions and statements} *)(****************************************************************)classtypesClass=objectinheritnopAslVisitorvalmutabletypes=IdentSet.emptymethodresult=typesmethod!vtypety=matchtywith|Type_Constructorid|Type_App(id,_)->types<-IdentSet.addidtypes;DoChildren|_->DoChildrenendlettypes_of_exprexpr=letcc=newtypesClassinignore(visit_expr(cc:>aslVisitor)expr);cc#resultlettypes_of_stmtsstmts=letcc=newtypesClassinignore(visit_stmts(cc:>aslVisitor)stmts);cc#resultlettypes_of_decldecl=letcc=newtypesClassinignore(visit_decl(cc:>aslVisitor)decl);cc#result(****************************************************************)(** {2 Calculate functions and procedures called in statements} *)(****************************************************************)classcallsClass=objectinheritnopAslVisitorvalmutablecalls=IdentSet.emptymethodresult=callsmethod!vexpr=function|Expr_TApply(f,_,_)->calls<-IdentSet.addfcalls;DoChildren|_->DoChildrenmethod!vstmt=function|Stmt_TCall(id,_,_,_)->calls<-IdentSet.addidcalls;DoChildren|_->DoChildrenmethod!vlexpr=function|LExpr_Write(id,_,_)->calls<-IdentSet.addidcalls;DoChildren|LExpr_ReadWrite(id1,id2,_,_)->calls<-IdentSet.addid1calls|>IdentSet.addid2;DoChildren|_->DoChildrenendletcalls_of_exprexpr=letcc=newcallsClassinignore(visit_expr(cc:>aslVisitor)expr);cc#resultletcalls_of_stmtsstmts=letcc=newcallsClassinignore(visit_stmts(cc:>aslVisitor)stmts);cc#resultletcalls_of_decldecl=letcc=newcallsClassinignore(visit_decl(cc:>aslVisitor)decl);cc#result(****************************************************************)(** {2 Substitutions} *)(****************************************************************)(** Performing variable substitutions in expressions and types
Note that it does not replace type constructors, global constants
or enumerations in patterns, array indexes and types so this is
limited to replacing local variables.
It also does not replace variables used as l-expressions though
that it easily changed if we think it should. *)classsubstClass(s:exprBindings.t)=objectinheritnopAslVisitormethod!vexprx=(matchxwith|Expr_Varv->(matchBindings.find_optvswith|Somer->ChangeTor|None->DoChildren)|_->DoChildren)endletsubst_expr(s:exprBindings.t)(x:expr):expr=letsubst=newsubstClasssinvisit_exprsubstxletsubst_lexpr(s:exprBindings.t)(x:lexpr):lexpr=letsubst=newsubstClasssinvisit_lexprsubstxletsubst_slice(s:exprBindings.t)(x:slice):slice=letsubst=newsubstClasssinvisit_slicesubstxletsubst_type(s:exprBindings.t)(x:ty):ty=letsubst=newsubstClasssinvisit_typesubstx(** More flexible substitution class - takes a function instead
of a binding set.
*)classsubstFunClass(replace:ident->exproption)=objectinheritnopAslVisitormethod!vexprx=(matchxwith|Expr_Varv->(matchreplacevwith|Somer->ChangeTor|None->DoChildren)|_->DoChildren)endletsubst_fun_expr(replace:ident->exproption)(x:expr):expr=letsubst=newsubstFunClassreplaceinvisit_exprsubstxletsubst_fun_lexpr(replace:ident->exproption)(x:lexpr):lexpr=letsubst=newsubstFunClassreplaceinvisit_lexprsubstxletsubst_fun_slice(replace:ident->exproption)(x:slice):slice=letsubst=newsubstFunClassreplaceinvisit_slicesubstxletsubst_fun_type(replace:ident->exproption)(x:ty):ty=letsubst=newsubstFunClassreplaceinvisit_typesubstx(****************************************************************)(** {2 Expression transformation} *)(****************************************************************)(** Expression transformation class
Applies replace function to any subexpression.
(Especially useful for expressions in types) *)classreplaceExprClass(replace:expr->exproption)=objectinheritnopAslVisitormethod!vexprx=(matchreplacexwith|Somer->ChangeTor|None->SkipChildren)end(****************************************************************)(** {2 Resugaring} *)(****************************************************************)(** Resugaring transform
The typechecker desugars infix syntax to make it absolutely explicit
what it means. This is good for tools but bad for humans.
This transformation re-introduces the infix syntax - the intention
being that you might use this in error messages.
It also deletes type parameters - so this is (more or less)
the reverse of typechecking. *)classresugarClass(ops:AST.binopBindings.t)=object(self)inheritnopAslVisitormethod!vexprx=(matchxwith|Expr_TApply(f,tys,args)->letargs'=List.map(visit_expr(self:>aslVisitor))argsin(match(Bindings.find_optfops,args')with|(Someop,[a;b])->ChangeTo(Expr_Binop(a,op,b))(* | (Some op, [a]) -> ChangeTo (Expr_Unop(op, a)) *)|_->ChangeTo(Expr_TApply(f,[],args')))|_->DoChildren)endletresugar_expr(ops:AST.binopBindings.t)(x:expr):expr=letresugar=newresugarClassopsinvisit_exprresugarxletresugar_type(ops:AST.binopBindings.t)(x:AST.ty):AST.ty=letresugar=newresugarClassopsinvisit_typeresugarx(****************************************************************)(** {2 Pretty printing wrappers} *)(****************************************************************)letpp_type(x:ty):string=Utils.to_string(PP.pp_tyx)letpp_expr(x:expr):string=Utils.to_string(PP.pp_exprx)letpp_lexpr(x:lexpr):string=Utils.to_string(PP.pp_lexprx)letpp_stmt(x:stmt):string=Utils.to_string(PP.pp_stmtx)(****************************************************************)(** {2 Misc} *)(****************************************************************)(** Length of bitstring or mask literal.
ASL bit and mask literals allow spaces to be included - these
do not count towards the length of the literal.
*)letmasklength(x:string):int=letr=ref0inString.iter(function' '->()|_->r:=!r+1)x;!r(****************************************************************
* End
****************************************************************)